@designfever/web-review-kit 0.8.1 → 0.8.3
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/README.md +4 -6
- package/dist/{chunk-4ZP7B7R6.js → chunk-FWN7RQCW.js} +190 -92
- package/dist/chunk-FWN7RQCW.js.map +1 -0
- package/dist/{chunk-UNDQZ4Y2.js → chunk-XP2IACXL.js} +80 -46
- package/dist/chunk-XP2IACXL.js.map +1 -0
- package/dist/index.cjs +268 -143
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +2 -2
- package/dist/index.d.ts +2 -2
- package/dist/index.js +3 -7
- package/dist/index.js.map +1 -1
- package/dist/react-shell.cjs +2627 -1736
- package/dist/react-shell.cjs.map +1 -1
- package/dist/react-shell.d.cts +2 -1
- package/dist/react-shell.d.ts +2 -1
- package/dist/react-shell.js +2247 -1447
- package/dist/react-shell.js.map +1 -1
- package/dist/{types-BM8E7BFV.d.cts → types-DKDtZjTH.d.cts} +8 -0
- package/dist/{types-BM8E7BFV.d.ts → types-DKDtZjTH.d.ts} +8 -0
- package/dist/vite.cjs +13 -5
- package/dist/vite.cjs.map +1 -1
- package/dist/vite.d.cts +2 -0
- package/dist/vite.d.ts +2 -0
- package/dist/vite.js +14 -6
- package/dist/vite.js.map +1 -1
- package/docs/README.md +4 -2
- package/docs/architecture.md +33 -7
- package/docs/figma-overlay.md +24 -0
- package/docs/installation.md +3 -5
- package/docs/release-notes-0.8.2.md +50 -0
- package/docs/release-notes-0.8.3.md +48 -0
- package/docs/testing.md +12 -4
- package/package.json +1 -1
- package/dist/chunk-4ZP7B7R6.js.map +0 -1
- package/dist/chunk-UNDQZ4Y2.js.map +0 -1
package/dist/vite.d.cts
CHANGED
|
@@ -48,6 +48,7 @@ declare const reviewFigmaImageStore: (options?: ReviewFigmaImageStorePluginOptio
|
|
|
48
48
|
|
|
49
49
|
type SourceLocatorPattern = string | RegExp;
|
|
50
50
|
interface ReviewSourceLocatorOptions {
|
|
51
|
+
/** @deprecated Dev server에서 자동 활성화되고 production build에서는 비활성화된다. */
|
|
51
52
|
enabled?: boolean;
|
|
52
53
|
root?: string;
|
|
53
54
|
include?: readonly SourceLocatorPattern[];
|
|
@@ -59,6 +60,7 @@ interface ReviewSourceLocatorOptions {
|
|
|
59
60
|
}
|
|
60
61
|
declare const reviewSourceLocator: (options?: ReviewSourceLocatorOptions) => Plugin;
|
|
61
62
|
interface ReviewDataLocatorOptions {
|
|
63
|
+
/** @deprecated Dev server에서 자동 활성화되고 production build에서는 비활성화된다. */
|
|
62
64
|
enabled?: boolean;
|
|
63
65
|
root?: string;
|
|
64
66
|
include?: readonly SourceLocatorPattern[];
|
package/dist/vite.d.ts
CHANGED
|
@@ -48,6 +48,7 @@ declare const reviewFigmaImageStore: (options?: ReviewFigmaImageStorePluginOptio
|
|
|
48
48
|
|
|
49
49
|
type SourceLocatorPattern = string | RegExp;
|
|
50
50
|
interface ReviewSourceLocatorOptions {
|
|
51
|
+
/** @deprecated Dev server에서 자동 활성화되고 production build에서는 비활성화된다. */
|
|
51
52
|
enabled?: boolean;
|
|
52
53
|
root?: string;
|
|
53
54
|
include?: readonly SourceLocatorPattern[];
|
|
@@ -59,6 +60,7 @@ interface ReviewSourceLocatorOptions {
|
|
|
59
60
|
}
|
|
60
61
|
declare const reviewSourceLocator: (options?: ReviewSourceLocatorOptions) => Plugin;
|
|
61
62
|
interface ReviewDataLocatorOptions {
|
|
63
|
+
/** @deprecated Dev server에서 자동 활성화되고 production build에서는 비활성화된다. */
|
|
62
64
|
enabled?: boolean;
|
|
63
65
|
root?: string;
|
|
64
66
|
include?: readonly SourceLocatorPattern[];
|
package/dist/vite.js
CHANGED
|
@@ -21,7 +21,10 @@ import {
|
|
|
21
21
|
readReviewFigmaToken,
|
|
22
22
|
renderReviewFigmaImage,
|
|
23
23
|
requireReviewFigmaToken
|
|
24
|
-
} from "./chunk-
|
|
24
|
+
} from "./chunk-XP2IACXL.js";
|
|
25
|
+
|
|
26
|
+
// src/vite/review-locator.mode.ts
|
|
27
|
+
var isReviewLocatorEnabled = (command) => command === "serve";
|
|
25
28
|
|
|
26
29
|
// src/vite/figma-image-store.ts
|
|
27
30
|
import path3 from "path";
|
|
@@ -906,16 +909,21 @@ var reviewSourceLocator = (options = {}) => {
|
|
|
906
909
|
return createJsxDevRuntime(runtimeOptions);
|
|
907
910
|
},
|
|
908
911
|
async transform(code, id) {
|
|
912
|
+
if (!runtimeOptions.enabled) return null;
|
|
909
913
|
const injectedCode = injectReviewSourceEnv(code, sourceEnvReplacements);
|
|
910
914
|
const inputCode = injectedCode ?? code;
|
|
911
|
-
const componentInjectedCode =
|
|
915
|
+
const componentInjectedCode = await injectReviewSourceComponentHints(
|
|
916
|
+
inputCode,
|
|
917
|
+
id,
|
|
918
|
+
runtimeOptions
|
|
919
|
+
);
|
|
912
920
|
return injectedCode || componentInjectedCode ? { code: componentInjectedCode ?? inputCode, map: null } : null;
|
|
913
921
|
}
|
|
914
922
|
};
|
|
915
923
|
};
|
|
916
924
|
var reviewDataLocator = (options = {}) => {
|
|
917
925
|
let root = normalizePath(options.root ?? "");
|
|
918
|
-
let enabled =
|
|
926
|
+
let enabled = false;
|
|
919
927
|
let sourceEnvReplacements = createReviewSourceEnvReplacements();
|
|
920
928
|
const include = (options.include ?? []).map(createRuntimeMatcher);
|
|
921
929
|
const exclude = (options.exclude ?? ["node_modules", "dist"]).map(
|
|
@@ -930,16 +938,16 @@ var reviewDataLocator = (options = {}) => {
|
|
|
930
938
|
enforce: "pre",
|
|
931
939
|
configResolved(config) {
|
|
932
940
|
root = normalizePath(options.root ?? config.root ?? "");
|
|
933
|
-
enabled =
|
|
941
|
+
enabled = isReviewLocatorEnabled(config.command);
|
|
934
942
|
sourceEnvReplacements = createReviewSourceEnvReplacements(config.env);
|
|
935
943
|
},
|
|
936
944
|
transform(code, id) {
|
|
945
|
+
if (!enabled) return null;
|
|
937
946
|
const envInjectedCode = injectReviewSourceEnv(
|
|
938
947
|
code,
|
|
939
948
|
sourceEnvReplacements
|
|
940
949
|
);
|
|
941
950
|
const inputCode = envInjectedCode ?? code;
|
|
942
|
-
if (!enabled) return null;
|
|
943
951
|
const file = normalizePath(id.split("?")[0]);
|
|
944
952
|
const relativeFile = root && file.startsWith(root + "/") ? file.slice(root.length + 1) : file;
|
|
945
953
|
if (include.length > 0 && !include.some((m) => matchesPath(m, file, relativeFile)))
|
|
@@ -1082,7 +1090,7 @@ function createRuntimeOptions(options, config) {
|
|
|
1082
1090
|
""
|
|
1083
1091
|
);
|
|
1084
1092
|
const root = normalizePath(options.root ?? config?.root ?? "");
|
|
1085
|
-
const enabled =
|
|
1093
|
+
const enabled = config ? isReviewLocatorEnabled(config.command) : false;
|
|
1086
1094
|
return {
|
|
1087
1095
|
enabled,
|
|
1088
1096
|
root,
|
package/dist/vite.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/vite/figma-image-store.ts","../src/vite/figma-image-store.server.ts","../src/vite/figma-image-store.image.ts","../src/vite.ts"],"sourcesContent":["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 assertTrustedReviewImageStoreRequest,\n handleReviewFigmaImageStoreRequest,\n isReviewImageStoreRequestError,\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 /** JSON 요청 body 상한(byte). 기본 25MB. */\n maxRequestBytes?: number;\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 // dev 서버는 인증이 없으므로 최소한 교차 출처 브라우저 요청은 막는다.\n assertTrustedReviewImageStoreRequest(req);\n\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, options.maxRequestBytes),\n requestToken: readRequestFigmaToken(req),\n });\n\n sendJson(res, response.status, response.body);\n } catch (error) {\n if (isReviewImageStoreRequestError(error)) {\n sendJson(res, error.status, { error: error.message });\n return;\n }\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","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 runExclusiveReviewImageStoreTask,\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 // mutation 분기들은 read-modify-write 전체를 락으로 감싼다.\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 return runExclusiveReviewImageStoreTask(dataFile, async () => {\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\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 return runExclusiveReviewImageStoreTask(dataFile, async () => {\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\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 return runExclusiveReviewImageStoreTask(dataFile, async () => {\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\n if (id && method === 'DELETE') {\n return runExclusiveReviewImageStoreTask(dataFile, async () => {\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\n return jsonError(405, 'method not allowed.');\n}\n\n// 첨부 이미지가 dataURL 로 JSON body 에 실려 오므로 넉넉하게 잡는다.\nexport const DEFAULT_REVIEW_IMAGE_STORE_MAX_BODY_BYTES = 25 * 1024 * 1024;\n\n/** 4xx 로 내려보낼 요청 검증 실패. 미들웨어 catch 에서 status 를 그대로 쓴다. */\nexport class ReviewImageStoreRequestError extends Error {\n constructor(\n readonly status: number,\n message: string\n ) {\n super(message);\n this.name = 'ReviewImageStoreRequestError';\n }\n}\n\nexport function isReviewImageStoreRequestError(\n error: unknown\n): error is ReviewImageStoreRequestError {\n return error instanceof ReviewImageStoreRequestError;\n}\n\n/**\n * CSRF 방어: 브라우저 요청(Origin 헤더 존재)은 요청 Host 와 같은 origin 이거나\n * loopback(localhost/127.0.0.1/::1)에서 온 것만 허용한다.\n * - LAN IP(폰 테스트 등) 접속: Origin.host === Host 라서 통과한다.\n * - curl/스크립트: Origin 헤더가 없으므로 통과한다. 악성 웹페이지는 브라우저가\n * Origin 을 강제로 붙이기 때문에 이 검사로 걸러진다.\n */\nexport function assertTrustedReviewImageStoreRequest(\n req: Pick<IncomingMessage, 'headers'>\n) {\n const origin = req.headers.origin;\n if (!origin || typeof origin !== 'string') return;\n\n let originUrl: URL;\n try {\n originUrl = new URL(origin);\n } catch {\n throw new ReviewImageStoreRequestError(\n 403,\n 'Cross-origin request is not allowed.'\n );\n }\n\n const host = req.headers.host;\n if (host && originUrl.host === host) return;\n if (isLoopbackHostname(originUrl.hostname)) return;\n\n throw new ReviewImageStoreRequestError(\n 403,\n 'Cross-origin request is not allowed.'\n );\n}\n\nfunction isLoopbackHostname(hostname: string) {\n return (\n hostname === 'localhost' ||\n hostname === '127.0.0.1' ||\n hostname === '::1' ||\n hostname === '[::1]'\n );\n}\n\nexport async function readJsonRequestBody(\n req: IncomingMessage,\n maxBytes = DEFAULT_REVIEW_IMAGE_STORE_MAX_BODY_BYTES\n) {\n if (req.method === 'GET' || req.method === 'DELETE') return null;\n\n const chunks: Buffer[] = [];\n let totalBytes = 0;\n for await (const chunk of req) {\n const buffer = typeof chunk === 'string' ? Buffer.from(chunk) : chunk;\n totalBytes += buffer.length;\n if (totalBytes > maxBytes) {\n throw new ReviewImageStoreRequestError(\n 413,\n `Request body exceeds ${maxBytes} bytes.`\n );\n }\n chunks.push(buffer);\n }\n const raw = Buffer.concat(chunks).toString('utf8').trim();\n if (!raw) return null;\n\n // CSRF 방어: CORS preflight 없이 보낼 수 있는 text/plain 류 body 를 거부한다.\n // 정상 클라이언트(image.store.ts)는 항상 application/json 을 보낸다.\n const contentType = req.headers['content-type'];\n if (\n typeof contentType !== 'string' ||\n !contentType.split(';')[0].trim().toLowerCase().endsWith('/json')\n ) {\n throw new ReviewImageStoreRequestError(\n 415,\n 'Request body must be application/json.'\n );\n }\n\n try {\n return JSON.parse(raw);\n } catch {\n throw new ReviewImageStoreRequestError(400, 'Invalid JSON request body.');\n }\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 { mkdir, readFile, rename, 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\n// dataFile 별 mutation 직렬화 큐.\n// 스토어 변경은 \"파일 읽기 → 수정 → 통째로 쓰기\"라서 두 요청이 겹치면\n// 나중 쓰기가 먼저 것을 덮어써 아이템이 유실된다. 같은 파일에 대한\n// mutation 을 프로미스 체인으로 순차 실행해 read-modify-write 를 원자화한다.\nconst reviewImageStoreTaskQueues = new Map<string, Promise<unknown>>();\n\nexport function runExclusiveReviewImageStoreTask<T>(\n dataFile: string,\n task: () => Promise<T>\n): Promise<T> {\n const previous =\n reviewImageStoreTaskQueues.get(dataFile) ?? Promise.resolve();\n // 앞 작업의 실패 여부와 무관하게 다음 작업은 실행되어야 한다.\n const result = previous.then(task, task);\n reviewImageStoreTaskQueues.set(\n dataFile,\n result.catch(() => undefined)\n );\n return result;\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 // temp 파일에 쓰고 rename 으로 교체(원자적).\n // writeFile 로 바로 덮어쓰면 truncate 이후 시점에 읽는 쪽이 잘린 JSON 을 본다.\n const tempFile = `${dataFile}.${process.pid}.${Date.now()}.tmp`;\n await writeFile(\n tempFile,\n `${JSON.stringify({ version: 1, images: data.images }, null, 2)}\\n`,\n 'utf8'\n );\n try {\n await rename(tempFile, dataFile);\n } catch (error) {\n await rm(tempFile, { force: true });\n throw error;\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","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 componentAttribute: string;\n parentFileAttribute: string;\n parentLineAttribute: string;\n parentColumnAttribute: string;\n parentComponentAttribute: string;\n};\n\ntype TypeScriptModule = typeof import('typescript');\n\ntype SourceComponentInsertion = {\n offset: number;\n value: string;\n};\n\nconst VIRTUAL_JSX_DEV_RUNTIME_ID =\n '\\0@designfever/web-review-kit/source-locator/jsx-dev-runtime';\n\nlet typescriptModulePromise: Promise<TypeScriptModule | null> | undefined;\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 async transform(code, id) {\n const injectedCode = injectReviewSourceEnv(code, sourceEnvReplacements);\n const inputCode = injectedCode ?? code;\n const componentInjectedCode = runtimeOptions.enabled\n ? await injectReviewSourceComponentHints(inputCode, id, runtimeOptions)\n : null;\n\n return injectedCode || componentInjectedCode\n ? { code: componentInjectedCode ?? inputCode, map: null }\n : 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\nasync function injectReviewSourceComponentHints(\n code: string,\n id: string,\n options: RuntimeOptions\n) {\n const file = normalizePath(id.split('?')[0]);\n if (!isJsxSourceFile(file, code)) return null;\n\n const relativeFile =\n options.root && file.startsWith(options.root + '/')\n ? file.slice(options.root.length + 1)\n : file;\n if (\n options.include.length > 0 &&\n !options.include.some((matcher) => matchesPath(matcher, file, relativeFile))\n ) {\n return null;\n }\n if (\n options.exclude.some((matcher) => matchesPath(matcher, file, relativeFile))\n ) {\n return null;\n }\n\n const ts = await loadTypeScript();\n if (!ts) return null;\n\n const sourceFile = ts.createSourceFile(\n file,\n code,\n ts.ScriptTarget.Latest,\n true,\n file.endsWith('.jsx') ? ts.ScriptKind.JSX : ts.ScriptKind.TSX\n );\n const insertions = getSourceComponentInsertions(\n ts,\n sourceFile,\n options.componentAttribute,\n options.parentComponentAttribute\n );\n if (insertions.length === 0) return null;\n\n return applySourceComponentInsertions(code, insertions);\n}\n\nasync function loadTypeScript() {\n const importTypeScript = new Function(\n 'specifier',\n 'return import(specifier)'\n ) as (specifier: string) => Promise<TypeScriptModule>;\n typescriptModulePromise ??= importTypeScript('typescript')\n .then((module) => module)\n .catch(() => null);\n return typescriptModulePromise;\n}\n\nfunction isJsxSourceFile(file: string, code: string) {\n return /\\.[cm]?[jt]sx$/.test(file) && code.includes('<');\n}\n\nfunction getSourceComponentInsertions(\n ts: TypeScriptModule,\n sourceFile: import('typescript').SourceFile,\n componentAttribute: string,\n parentComponentAttribute: string\n) {\n const insertions: SourceComponentInsertion[] = [];\n\n const visit = (\n node: import('typescript').Node,\n currentComponent: string | undefined\n ) => {\n const component = getComponentNameForNode(ts, node) ?? currentComponent;\n if (\n component &&\n isIntrinsicJsxElement(ts, node, sourceFile) &&\n !hasJsxAttribute(ts, node, componentAttribute) &&\n !hasJsxAttribute(ts, node, 'data-component')\n ) {\n insertions.push({\n offset: node.tagName.end,\n value: ` ${componentAttribute}=${JSON.stringify(component)}`,\n });\n }\n if (\n component &&\n isCustomJsxElement(ts, node, sourceFile) &&\n !hasJsxAttribute(ts, node, parentComponentAttribute)\n ) {\n insertions.push({\n offset: node.tagName.end,\n value: ` ${parentComponentAttribute}=${JSON.stringify(component)}`,\n });\n }\n\n ts.forEachChild(node, (child) => visit(child, component));\n };\n\n visit(sourceFile, undefined);\n return insertions;\n}\n\nfunction getComponentNameForNode(\n ts: TypeScriptModule,\n node: import('typescript').Node\n) {\n if (ts.isFunctionDeclaration(node) || ts.isClassDeclaration(node)) {\n const name = node.name?.text;\n return isComponentName(name) ? name : undefined;\n }\n\n if (ts.isVariableDeclaration(node) && ts.isIdentifier(node.name)) {\n const name = node.name.text;\n return isComponentName(name) && node.initializer ? name : undefined;\n }\n\n return undefined;\n}\n\nfunction isIntrinsicJsxElement(\n ts: TypeScriptModule,\n node: import('typescript').Node,\n sourceFile: import('typescript').SourceFile\n): node is import('typescript').JsxOpeningLikeElement {\n if (!ts.isJsxOpeningElement(node) && !ts.isJsxSelfClosingElement(node)) {\n return false;\n }\n\n const tagName = node.tagName.getText(sourceFile);\n return /^[a-z]/.test(tagName) || tagName.includes('-');\n}\n\nfunction isCustomJsxElement(\n ts: TypeScriptModule,\n node: import('typescript').Node,\n sourceFile: import('typescript').SourceFile\n): node is import('typescript').JsxOpeningLikeElement {\n if (!ts.isJsxOpeningElement(node) && !ts.isJsxSelfClosingElement(node)) {\n return false;\n }\n\n const tagName = node.tagName.getText(sourceFile);\n if (isReactRuntimeElementName(tagName)) return false;\n return /^[A-Z]/.test(tagName);\n}\n\nfunction isReactRuntimeElementName(tagName: string) {\n const name = tagName.startsWith('React.')\n ? tagName.slice('React.'.length)\n : tagName;\n return name === 'Fragment' || name === 'StrictMode' || name === 'Profiler';\n}\n\nfunction hasJsxAttribute(\n ts: TypeScriptModule,\n node: import('typescript').JsxOpeningLikeElement,\n name: string\n) {\n return node.attributes.properties.some(\n (property) =>\n ts.isJsxAttribute(property) && property.name.getText() === name\n );\n}\n\nfunction isComponentName(name: string | undefined) {\n return Boolean(name && /^[A-Z][A-Za-z0-9_$]*$/.test(name));\n}\n\nfunction applySourceComponentInsertions(\n code: string,\n insertions: SourceComponentInsertion[]\n) {\n return insertions\n .slice()\n .sort((a, b) => b.offset - a.offset)\n .reduce(\n (nextCode, insertion) =>\n `${nextCode.slice(0, insertion.offset)}${insertion.value}${nextCode.slice(\n insertion.offset\n )}`,\n code\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 componentAttribute: `${attributePrefix}-component`,\n parentFileAttribute: `${attributePrefix}-parent-file`,\n parentLineAttribute: `${attributePrefix}-parent-line`,\n parentColumnAttribute: `${attributePrefix}-parent-column`,\n parentComponentAttribute: `${attributePrefix}-parent-component`,\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)};\nconst sourceUsageStack = [];\nconst sourceUsageWrapperCache = new WeakMap();\n\nexport { Fragment };\n\nexport function jsxDEV(type, props, key, isStaticChildren, source, self) {\n const sourceUsage = getSourceUsage(type, props, source);\n const nextType = sourceUsage ? getSourceUsageWrapper(type, sourceUsage) : type;\n return baseJsxDEV(\n nextType,\n injectSourceProps(type, props, source, sourceUsage),\n key,\n isStaticChildren,\n source,\n self\n );\n}\n\nfunction injectSourceProps(type, props, source, sourceUsage) {\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 (typeof type !== 'string') {\n injectParentSourceProps(nextProps, sourceUsage);\n return nextProps;\n }\n\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 injectParentSourceProps(nextProps, getCurrentSourceUsage());\n\n return nextProps;\n}\n\nfunction getSourceUsage(type, props, source) {\n if (!isSourceUsageComponentType(type)) return null;\n if (!source || typeof source.fileName !== 'string') return null;\n\n const sourceFile = getSourceFile(source.fileName);\n if (!sourceFile) return null;\n\n return {\n file: sourceFile,\n line: OPTIONS.line && source.lineNumber != null ? String(source.lineNumber) : '',\n column: OPTIONS.column && source.columnNumber != null ? String(source.columnNumber) : '',\n component: readSourceUsageComponent(props),\n };\n}\n\nfunction isSourceUsageComponentType(type) {\n return (\n typeof type === 'function' &&\n !isClassComponent(type)\n );\n}\n\nfunction isClassComponent(type) {\n return Boolean(type?.prototype?.isReactComponent);\n}\n\nfunction getSourceUsageWrapper(type, usage) {\n let wrappers = sourceUsageWrapperCache.get(type);\n if (!wrappers) {\n wrappers = new Map();\n sourceUsageWrapperCache.set(type, wrappers);\n }\n\n const key = getSourceUsageKey(usage);\n const existing = wrappers.get(key);\n if (existing) return existing;\n\n const wrapped = function ReviewSourceUsageWrapper(props) {\n sourceUsageStack.push(usage);\n try {\n return type(props);\n } finally {\n sourceUsageStack.pop();\n }\n };\n wrapped.displayName = 'ReviewSourceUsage(' + getComponentDisplayName(type) + ')';\n wrappers.set(key, wrapped);\n return wrapped;\n}\n\nfunction getComponentDisplayName(type) {\n return type.displayName || type.name || 'Component';\n}\n\nfunction getSourceUsageKey(usage) {\n return [\n usage.file,\n usage.line,\n usage.column,\n usage.component,\n ].join('|');\n}\n\nfunction getCurrentSourceUsage() {\n return sourceUsageStack[sourceUsageStack.length - 1] || null;\n}\n\nfunction readSourceUsageComponent(props) {\n const value = props?.[OPTIONS.parentComponentAttribute];\n return typeof value === 'string' ? value : '';\n}\n\nfunction injectParentSourceProps(props, usage) {\n if (!usage?.file) return;\n\n if (props[OPTIONS.parentFileAttribute] == null) {\n props[OPTIONS.parentFileAttribute] = usage.file;\n }\n if (usage.line && props[OPTIONS.parentLineAttribute] == null) {\n props[OPTIONS.parentLineAttribute] = usage.line;\n }\n if (usage.column && props[OPTIONS.parentColumnAttribute] == null) {\n props[OPTIONS.parentColumnAttribute] = usage.column;\n }\n if (usage.component && props[OPTIONS.parentComponentAttribute] == null) {\n props[OPTIONS.parentComponentAttribute] = usage.component;\n }\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"],"mappings":"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","names":["path","readFile","path","cachedAsset","now","order","readFile","path","path"]}
|
|
1
|
+
{"version":3,"sources":["../src/vite/review-locator.mode.ts","../src/vite/figma-image-store.ts","../src/vite/figma-image-store.server.ts","../src/vite/figma-image-store.image.ts","../src/vite.ts"],"sourcesContent":["export const isReviewLocatorEnabled = (command: 'serve' | 'build') =>\n command === 'serve';\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 assertTrustedReviewImageStoreRequest,\n handleReviewFigmaImageStoreRequest,\n isReviewImageStoreRequestError,\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 /** JSON 요청 body 상한(byte). 기본 25MB. */\n maxRequestBytes?: number;\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 // dev 서버는 인증이 없으므로 최소한 교차 출처 브라우저 요청은 막는다.\n assertTrustedReviewImageStoreRequest(req);\n\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, options.maxRequestBytes),\n requestToken: readRequestFigmaToken(req),\n });\n\n sendJson(res, response.status, response.body);\n } catch (error) {\n if (isReviewImageStoreRequestError(error)) {\n sendJson(res, error.status, { error: error.message });\n return;\n }\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","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 runExclusiveReviewImageStoreTask,\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 // mutation 분기들은 read-modify-write 전체를 락으로 감싼다.\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 return runExclusiveReviewImageStoreTask(dataFile, async () => {\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\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 return runExclusiveReviewImageStoreTask(dataFile, async () => {\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\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 return runExclusiveReviewImageStoreTask(dataFile, async () => {\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\n if (id && method === 'DELETE') {\n return runExclusiveReviewImageStoreTask(dataFile, async () => {\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\n return jsonError(405, 'method not allowed.');\n}\n\n// 첨부 이미지가 dataURL 로 JSON body 에 실려 오므로 넉넉하게 잡는다.\nexport const DEFAULT_REVIEW_IMAGE_STORE_MAX_BODY_BYTES = 25 * 1024 * 1024;\n\n/** 4xx 로 내려보낼 요청 검증 실패. 미들웨어 catch 에서 status 를 그대로 쓴다. */\nexport class ReviewImageStoreRequestError extends Error {\n constructor(\n readonly status: number,\n message: string\n ) {\n super(message);\n this.name = 'ReviewImageStoreRequestError';\n }\n}\n\nexport function isReviewImageStoreRequestError(\n error: unknown\n): error is ReviewImageStoreRequestError {\n return error instanceof ReviewImageStoreRequestError;\n}\n\n/**\n * CSRF 방어: 브라우저 요청(Origin 헤더 존재)은 요청 Host 와 같은 origin 이거나\n * loopback(localhost/127.0.0.1/::1)에서 온 것만 허용한다.\n * - LAN IP(폰 테스트 등) 접속: Origin.host === Host 라서 통과한다.\n * - curl/스크립트: Origin 헤더가 없으므로 통과한다. 악성 웹페이지는 브라우저가\n * Origin 을 강제로 붙이기 때문에 이 검사로 걸러진다.\n */\nexport function assertTrustedReviewImageStoreRequest(\n req: Pick<IncomingMessage, 'headers'>\n) {\n const origin = req.headers.origin;\n if (!origin || typeof origin !== 'string') return;\n\n let originUrl: URL;\n try {\n originUrl = new URL(origin);\n } catch {\n throw new ReviewImageStoreRequestError(\n 403,\n 'Cross-origin request is not allowed.'\n );\n }\n\n const host = req.headers.host;\n if (host && originUrl.host === host) return;\n if (isLoopbackHostname(originUrl.hostname)) return;\n\n throw new ReviewImageStoreRequestError(\n 403,\n 'Cross-origin request is not allowed.'\n );\n}\n\nfunction isLoopbackHostname(hostname: string) {\n return (\n hostname === 'localhost' ||\n hostname === '127.0.0.1' ||\n hostname === '::1' ||\n hostname === '[::1]'\n );\n}\n\nexport async function readJsonRequestBody(\n req: IncomingMessage,\n maxBytes = DEFAULT_REVIEW_IMAGE_STORE_MAX_BODY_BYTES\n) {\n if (req.method === 'GET' || req.method === 'DELETE') return null;\n\n const chunks: Buffer[] = [];\n let totalBytes = 0;\n for await (const chunk of req) {\n const buffer = typeof chunk === 'string' ? Buffer.from(chunk) : chunk;\n totalBytes += buffer.length;\n if (totalBytes > maxBytes) {\n throw new ReviewImageStoreRequestError(\n 413,\n `Request body exceeds ${maxBytes} bytes.`\n );\n }\n chunks.push(buffer);\n }\n const raw = Buffer.concat(chunks).toString('utf8').trim();\n if (!raw) return null;\n\n // CSRF 방어: CORS preflight 없이 보낼 수 있는 text/plain 류 body 를 거부한다.\n // 정상 클라이언트(image.store.ts)는 항상 application/json 을 보낸다.\n const contentType = req.headers['content-type'];\n if (\n typeof contentType !== 'string' ||\n !contentType.split(';')[0].trim().toLowerCase().endsWith('/json')\n ) {\n throw new ReviewImageStoreRequestError(\n 415,\n 'Request body must be application/json.'\n );\n }\n\n try {\n return JSON.parse(raw);\n } catch {\n throw new ReviewImageStoreRequestError(400, 'Invalid JSON request body.');\n }\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 { mkdir, readFile, rename, 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\n// dataFile 별 mutation 직렬화 큐.\n// 스토어 변경은 \"파일 읽기 → 수정 → 통째로 쓰기\"라서 두 요청이 겹치면\n// 나중 쓰기가 먼저 것을 덮어써 아이템이 유실된다. 같은 파일에 대한\n// mutation 을 프로미스 체인으로 순차 실행해 read-modify-write 를 원자화한다.\nconst reviewImageStoreTaskQueues = new Map<string, Promise<unknown>>();\n\nexport function runExclusiveReviewImageStoreTask<T>(\n dataFile: string,\n task: () => Promise<T>\n): Promise<T> {\n const previous =\n reviewImageStoreTaskQueues.get(dataFile) ?? Promise.resolve();\n // 앞 작업의 실패 여부와 무관하게 다음 작업은 실행되어야 한다.\n const result = previous.then(task, task);\n reviewImageStoreTaskQueues.set(\n dataFile,\n result.catch(() => undefined)\n );\n return result;\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 // temp 파일에 쓰고 rename 으로 교체(원자적).\n // writeFile 로 바로 덮어쓰면 truncate 이후 시점에 읽는 쪽이 잘린 JSON 을 본다.\n const tempFile = `${dataFile}.${process.pid}.${Date.now()}.tmp`;\n await writeFile(\n tempFile,\n `${JSON.stringify({ version: 1, images: data.images }, null, 2)}\\n`,\n 'utf8'\n );\n try {\n await rename(tempFile, dataFile);\n } catch (error) {\n await rm(tempFile, { force: true });\n throw error;\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","import { type Plugin, type ResolvedConfig } from 'vite';\nimport { isReviewLocatorEnabled } from './vite/review-locator.mode';\n\nexport * from './vite/figma-image-store';\n\ntype SourceLocatorPattern = string | RegExp;\n\nexport interface ReviewSourceLocatorOptions {\n /** @deprecated Dev server에서 자동 활성화되고 production build에서는 비활성화된다. */\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 componentAttribute: string;\n parentFileAttribute: string;\n parentLineAttribute: string;\n parentColumnAttribute: string;\n parentComponentAttribute: string;\n};\n\ntype TypeScriptModule = typeof import('typescript');\n\ntype SourceComponentInsertion = {\n offset: number;\n value: string;\n};\n\nconst VIRTUAL_JSX_DEV_RUNTIME_ID =\n '\\0@designfever/web-review-kit/source-locator/jsx-dev-runtime';\n\nlet typescriptModulePromise: Promise<TypeScriptModule | null> | undefined;\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 async transform(code, id) {\n if (!runtimeOptions.enabled) return null;\n\n const injectedCode = injectReviewSourceEnv(code, sourceEnvReplacements);\n const inputCode = injectedCode ?? code;\n const componentInjectedCode = await injectReviewSourceComponentHints(\n inputCode,\n id,\n runtimeOptions\n );\n\n return injectedCode || componentInjectedCode\n ? { code: componentInjectedCode ?? inputCode, map: null }\n : null;\n },\n };\n};\n\nexport interface ReviewDataLocatorOptions {\n /** @deprecated Dev server에서 자동 활성화되고 production build에서는 비활성화된다. */\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 = 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 = isReviewLocatorEnabled(config.command);\n sourceEnvReplacements = createReviewSourceEnvReplacements(config.env);\n },\n transform(code, id) {\n if (!enabled) return null;\n\n const envInjectedCode = injectReviewSourceEnv(\n code,\n sourceEnvReplacements\n );\n const inputCode = envInjectedCode ?? code;\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\nasync function injectReviewSourceComponentHints(\n code: string,\n id: string,\n options: RuntimeOptions\n) {\n const file = normalizePath(id.split('?')[0]);\n if (!isJsxSourceFile(file, code)) return null;\n\n const relativeFile =\n options.root && file.startsWith(options.root + '/')\n ? file.slice(options.root.length + 1)\n : file;\n if (\n options.include.length > 0 &&\n !options.include.some((matcher) => matchesPath(matcher, file, relativeFile))\n ) {\n return null;\n }\n if (\n options.exclude.some((matcher) => matchesPath(matcher, file, relativeFile))\n ) {\n return null;\n }\n\n const ts = await loadTypeScript();\n if (!ts) return null;\n\n const sourceFile = ts.createSourceFile(\n file,\n code,\n ts.ScriptTarget.Latest,\n true,\n file.endsWith('.jsx') ? ts.ScriptKind.JSX : ts.ScriptKind.TSX\n );\n const insertions = getSourceComponentInsertions(\n ts,\n sourceFile,\n options.componentAttribute,\n options.parentComponentAttribute\n );\n if (insertions.length === 0) return null;\n\n return applySourceComponentInsertions(code, insertions);\n}\n\nasync function loadTypeScript() {\n const importTypeScript = new Function(\n 'specifier',\n 'return import(specifier)'\n ) as (specifier: string) => Promise<TypeScriptModule>;\n typescriptModulePromise ??= importTypeScript('typescript')\n .then((module) => module)\n .catch(() => null);\n return typescriptModulePromise;\n}\n\nfunction isJsxSourceFile(file: string, code: string) {\n return /\\.[cm]?[jt]sx$/.test(file) && code.includes('<');\n}\n\nfunction getSourceComponentInsertions(\n ts: TypeScriptModule,\n sourceFile: import('typescript').SourceFile,\n componentAttribute: string,\n parentComponentAttribute: string\n) {\n const insertions: SourceComponentInsertion[] = [];\n\n const visit = (\n node: import('typescript').Node,\n currentComponent: string | undefined\n ) => {\n const component = getComponentNameForNode(ts, node) ?? currentComponent;\n if (\n component &&\n isIntrinsicJsxElement(ts, node, sourceFile) &&\n !hasJsxAttribute(ts, node, componentAttribute) &&\n !hasJsxAttribute(ts, node, 'data-component')\n ) {\n insertions.push({\n offset: node.tagName.end,\n value: ` ${componentAttribute}=${JSON.stringify(component)}`,\n });\n }\n if (\n component &&\n isCustomJsxElement(ts, node, sourceFile) &&\n !hasJsxAttribute(ts, node, parentComponentAttribute)\n ) {\n insertions.push({\n offset: node.tagName.end,\n value: ` ${parentComponentAttribute}=${JSON.stringify(component)}`,\n });\n }\n\n ts.forEachChild(node, (child) => visit(child, component));\n };\n\n visit(sourceFile, undefined);\n return insertions;\n}\n\nfunction getComponentNameForNode(\n ts: TypeScriptModule,\n node: import('typescript').Node\n) {\n if (ts.isFunctionDeclaration(node) || ts.isClassDeclaration(node)) {\n const name = node.name?.text;\n return isComponentName(name) ? name : undefined;\n }\n\n if (ts.isVariableDeclaration(node) && ts.isIdentifier(node.name)) {\n const name = node.name.text;\n return isComponentName(name) && node.initializer ? name : undefined;\n }\n\n return undefined;\n}\n\nfunction isIntrinsicJsxElement(\n ts: TypeScriptModule,\n node: import('typescript').Node,\n sourceFile: import('typescript').SourceFile\n): node is import('typescript').JsxOpeningLikeElement {\n if (!ts.isJsxOpeningElement(node) && !ts.isJsxSelfClosingElement(node)) {\n return false;\n }\n\n const tagName = node.tagName.getText(sourceFile);\n return /^[a-z]/.test(tagName) || tagName.includes('-');\n}\n\nfunction isCustomJsxElement(\n ts: TypeScriptModule,\n node: import('typescript').Node,\n sourceFile: import('typescript').SourceFile\n): node is import('typescript').JsxOpeningLikeElement {\n if (!ts.isJsxOpeningElement(node) && !ts.isJsxSelfClosingElement(node)) {\n return false;\n }\n\n const tagName = node.tagName.getText(sourceFile);\n if (isReactRuntimeElementName(tagName)) return false;\n return /^[A-Z]/.test(tagName);\n}\n\nfunction isReactRuntimeElementName(tagName: string) {\n const name = tagName.startsWith('React.')\n ? tagName.slice('React.'.length)\n : tagName;\n return name === 'Fragment' || name === 'StrictMode' || name === 'Profiler';\n}\n\nfunction hasJsxAttribute(\n ts: TypeScriptModule,\n node: import('typescript').JsxOpeningLikeElement,\n name: string\n) {\n return node.attributes.properties.some(\n (property) =>\n ts.isJsxAttribute(property) && property.name.getText() === name\n );\n}\n\nfunction isComponentName(name: string | undefined) {\n return Boolean(name && /^[A-Z][A-Za-z0-9_$]*$/.test(name));\n}\n\nfunction applySourceComponentInsertions(\n code: string,\n insertions: SourceComponentInsertion[]\n) {\n return insertions\n .slice()\n .sort((a, b) => b.offset - a.offset)\n .reduce(\n (nextCode, insertion) =>\n `${nextCode.slice(0, insertion.offset)}${insertion.value}${nextCode.slice(\n insertion.offset\n )}`,\n code\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 = config\n ? isReviewLocatorEnabled(config.command)\n : false;\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 componentAttribute: `${attributePrefix}-component`,\n parentFileAttribute: `${attributePrefix}-parent-file`,\n parentLineAttribute: `${attributePrefix}-parent-line`,\n parentColumnAttribute: `${attributePrefix}-parent-column`,\n parentComponentAttribute: `${attributePrefix}-parent-component`,\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)};\nconst sourceUsageStack = [];\nconst sourceUsageWrapperCache = new WeakMap();\n\nexport { Fragment };\n\nexport function jsxDEV(type, props, key, isStaticChildren, source, self) {\n const sourceUsage = getSourceUsage(type, props, source);\n const nextType = sourceUsage ? getSourceUsageWrapper(type, sourceUsage) : type;\n return baseJsxDEV(\n nextType,\n injectSourceProps(type, props, source, sourceUsage),\n key,\n isStaticChildren,\n source,\n self\n );\n}\n\nfunction injectSourceProps(type, props, source, sourceUsage) {\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 (typeof type !== 'string') {\n injectParentSourceProps(nextProps, sourceUsage);\n return nextProps;\n }\n\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 injectParentSourceProps(nextProps, getCurrentSourceUsage());\n\n return nextProps;\n}\n\nfunction getSourceUsage(type, props, source) {\n if (!isSourceUsageComponentType(type)) return null;\n if (!source || typeof source.fileName !== 'string') return null;\n\n const sourceFile = getSourceFile(source.fileName);\n if (!sourceFile) return null;\n\n return {\n file: sourceFile,\n line: OPTIONS.line && source.lineNumber != null ? String(source.lineNumber) : '',\n column: OPTIONS.column && source.columnNumber != null ? String(source.columnNumber) : '',\n component: readSourceUsageComponent(props),\n };\n}\n\nfunction isSourceUsageComponentType(type) {\n return (\n typeof type === 'function' &&\n !isClassComponent(type)\n );\n}\n\nfunction isClassComponent(type) {\n return Boolean(type?.prototype?.isReactComponent);\n}\n\nfunction getSourceUsageWrapper(type, usage) {\n let wrappers = sourceUsageWrapperCache.get(type);\n if (!wrappers) {\n wrappers = new Map();\n sourceUsageWrapperCache.set(type, wrappers);\n }\n\n const key = getSourceUsageKey(usage);\n const existing = wrappers.get(key);\n if (existing) return existing;\n\n const wrapped = function ReviewSourceUsageWrapper(props) {\n sourceUsageStack.push(usage);\n try {\n return type(props);\n } finally {\n sourceUsageStack.pop();\n }\n };\n wrapped.displayName = 'ReviewSourceUsage(' + getComponentDisplayName(type) + ')';\n wrappers.set(key, wrapped);\n return wrapped;\n}\n\nfunction getComponentDisplayName(type) {\n return type.displayName || type.name || 'Component';\n}\n\nfunction getSourceUsageKey(usage) {\n return [\n usage.file,\n usage.line,\n usage.column,\n usage.component,\n ].join('|');\n}\n\nfunction getCurrentSourceUsage() {\n return sourceUsageStack[sourceUsageStack.length - 1] || null;\n}\n\nfunction readSourceUsageComponent(props) {\n const value = props?.[OPTIONS.parentComponentAttribute];\n return typeof value === 'string' ? value : '';\n}\n\nfunction injectParentSourceProps(props, usage) {\n if (!usage?.file) return;\n\n if (props[OPTIONS.parentFileAttribute] == null) {\n props[OPTIONS.parentFileAttribute] = usage.file;\n }\n if (usage.line && props[OPTIONS.parentLineAttribute] == null) {\n props[OPTIONS.parentLineAttribute] = usage.line;\n }\n if (usage.column && props[OPTIONS.parentColumnAttribute] == null) {\n props[OPTIONS.parentColumnAttribute] = usage.column;\n }\n if (usage.component && props[OPTIONS.parentComponentAttribute] == null) {\n props[OPTIONS.parentComponentAttribute] = usage.component;\n }\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"],"mappings":"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","names":["path","readFile","path","cachedAsset","now","order","readFile","path","path"]}
|
package/docs/README.md
CHANGED
|
@@ -22,12 +22,14 @@ Public docs are intentionally small. Keep implementation history, handoff notes,
|
|
|
22
22
|
- `testing.md`: Vitest adapter contract and core pure-function unit coverage, plus local verification commands.
|
|
23
23
|
- `adaptor.sample.ts`: copyable starting point for host-owned remote adapters.
|
|
24
24
|
- `db-setup.md`: optional Supabase review item table/RPC/RLS/presence setup.
|
|
25
|
-
- `architecture.md`: core/runtime, React shell, coordinate, anchor, and feature
|
|
26
|
-
- `figma-overlay.md`: host requirements
|
|
25
|
+
- `architecture.md`: core/runtime, React shell, coordinate, anchor, sitemap, and feature ownership boundaries.
|
|
26
|
+
- `figma-overlay.md`: host helper requirements plus package image overlay state and interaction rules.
|
|
27
27
|
- `grid-overlay.md`: host requirements for the grid/helper overlay toggle.
|
|
28
28
|
|
|
29
29
|
## Release History
|
|
30
30
|
|
|
31
|
+
- [0.8.3](release-notes-0.8.3.md): code-review bug fixes (hidden draft composer, select hotkeys, overlay state writes, URL hash), sitemap state persistence/filtering, tests, and feature-module cleanup.
|
|
32
|
+
- [0.8.2](release-notes-0.8.2.md): Figma image layer edit/delete tooltip removal.
|
|
31
33
|
- [0.8.1](release-notes-0.8.1.md): QA prompt prefix, QA filtering, counters, tooltip, long-content, and outside marker fixes.
|
|
32
34
|
- [0.8.0](release-notes-0.8.0.md): next minor release notes for QA attachments, adapter contract, capture, source inspector, and dev fixture changes.
|
|
33
35
|
- [0.7.3](release-notes-0.7.3.md): endpoint Figma image store API and validation scope.
|