@designfever/web-review-kit 0.7.1 → 0.7.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 +1 -1
- package/dist/{chunk-2ZLU5FTD.js → chunk-UNDQZ4Y2.js} +51 -13
- package/dist/chunk-UNDQZ4Y2.js.map +1 -0
- package/dist/index.cjs +348 -12
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +71 -1
- package/dist/index.d.ts +71 -1
- package/dist/index.js +300 -1
- package/dist/index.js.map +1 -1
- package/dist/vite.cjs.map +1 -1
- package/dist/vite.js +1 -1
- package/docs/README.md +7 -3
- package/docs/adapters.md +58 -1
- package/docs/release-notes-0.7.2.md +33 -0
- package/docs/release-notes-0.7.3.md +30 -0
- package/package.json +1 -1
- package/dist/chunk-2ZLU5FTD.js.map +0 -1
package/README.md
CHANGED
|
@@ -28,7 +28,7 @@ This package does not own internal operator tools, private admin keys, or produc
|
|
|
28
28
|
- [Architecture and runtime logic](docs/architecture.md): core runtime, React shell, coordinate, anchor, and extension boundaries.
|
|
29
29
|
- [Figma overlay](docs/figma-overlay.md): how the shell toggles a host Figma overlay.
|
|
30
30
|
- [Grid overlay](docs/grid-overlay.md): how the shell toggles a host grid/helper overlay.
|
|
31
|
-
- [Release notes 0.7.
|
|
31
|
+
- [Release notes 0.7.3](docs/release-notes-0.7.3.md): latest endpoint Figma image store changes.
|
|
32
32
|
|
|
33
33
|
## Quick Start
|
|
34
34
|
|
|
@@ -217,8 +217,14 @@ function getReviewFigmaAssetMimeType(storageKey) {
|
|
|
217
217
|
// src/figma/image.store.ts
|
|
218
218
|
var DEFAULT_REVIEW_FIGMA_IMAGE_STORE_ENDPOINT = "/__dfwr/figma-images";
|
|
219
219
|
function createReviewFigmaImageStoreClient(options = {}) {
|
|
220
|
+
return createEndpointReviewFigmaImageStore(options);
|
|
221
|
+
}
|
|
222
|
+
function createEndpointReviewFigmaImageStore(options = {}) {
|
|
220
223
|
const endpoint = options.endpoint ?? DEFAULT_REVIEW_FIGMA_IMAGE_STORE_ENDPOINT;
|
|
221
|
-
const request = createReviewFigmaImageStoreRequest(
|
|
224
|
+
const request = createReviewFigmaImageStoreRequest(
|
|
225
|
+
options.fetch,
|
|
226
|
+
options.headers
|
|
227
|
+
);
|
|
222
228
|
return {
|
|
223
229
|
listImages(target) {
|
|
224
230
|
const url = `${endpoint}?target=${encodeURIComponent(
|
|
@@ -265,10 +271,12 @@ async function createClientRenderedAddImageInput(input, clientRender, fetchOptio
|
|
|
265
271
|
const token = readClientRenderToken(options);
|
|
266
272
|
if (!token) return input;
|
|
267
273
|
try {
|
|
268
|
-
const asset = await
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
274
|
+
const asset = await createReviewFigmaClientRenderedAsset({
|
|
275
|
+
...options,
|
|
276
|
+
fetch: fetchOption,
|
|
277
|
+
figmaUrl: input.figmaUrl,
|
|
278
|
+
token
|
|
279
|
+
});
|
|
272
280
|
return {
|
|
273
281
|
...input,
|
|
274
282
|
imageFormat: asset.imageFormat,
|
|
@@ -340,6 +348,17 @@ async function createClientRenderedFigmaAsset(figmaUrl, token, options, fetchOpt
|
|
|
340
348
|
height: dimensions.height
|
|
341
349
|
};
|
|
342
350
|
}
|
|
351
|
+
function createReviewFigmaClientRenderedAsset({
|
|
352
|
+
fetch: fetchOption,
|
|
353
|
+
figmaUrl,
|
|
354
|
+
token,
|
|
355
|
+
...options
|
|
356
|
+
}) {
|
|
357
|
+
return withTimeout(
|
|
358
|
+
createClientRenderedFigmaAsset(figmaUrl, token, options, fetchOption),
|
|
359
|
+
options.timeoutMs ?? 1e4
|
|
360
|
+
);
|
|
361
|
+
}
|
|
343
362
|
async function readImageBlobDimensions(blob) {
|
|
344
363
|
const image = await loadImageBlob(blob);
|
|
345
364
|
return {
|
|
@@ -426,19 +445,27 @@ function getReviewFigmaImageMimeType(format) {
|
|
|
426
445
|
if (format === "png") return "image/png";
|
|
427
446
|
return "image/webp";
|
|
428
447
|
}
|
|
429
|
-
function createReviewFigmaImageStoreRequest(
|
|
448
|
+
function createReviewFigmaImageStoreRequest(fetchOption, headersProvider) {
|
|
430
449
|
return async (input, init = {}) => {
|
|
431
450
|
const requestFetch = fetchOption ?? globalThis.fetch;
|
|
432
451
|
if (!requestFetch) throw new Error("Figma image store requires fetch.");
|
|
433
452
|
const figmaToken = init.figmaToken ?? "";
|
|
434
|
-
const {
|
|
453
|
+
const {
|
|
454
|
+
figmaToken: _figmaToken,
|
|
455
|
+
headers: requestHeaders,
|
|
456
|
+
...requestInit
|
|
457
|
+
} = init;
|
|
458
|
+
const headers = new Headers();
|
|
459
|
+
headers.set("Content-Type", "application/json");
|
|
460
|
+
if (figmaToken) headers.set("X-Figma-Token", figmaToken);
|
|
461
|
+
appendReviewFigmaImageStoreHeaders(
|
|
462
|
+
headers,
|
|
463
|
+
await readReviewFigmaImageStoreHeaders(headersProvider)
|
|
464
|
+
);
|
|
465
|
+
appendReviewFigmaImageStoreHeaders(headers, requestHeaders);
|
|
435
466
|
const response = await requestFetch(input, {
|
|
436
467
|
...requestInit,
|
|
437
|
-
headers
|
|
438
|
-
"Content-Type": "application/json",
|
|
439
|
-
...figmaToken ? { "X-Figma-Token": figmaToken } : {},
|
|
440
|
-
...requestInit.headers ?? {}
|
|
441
|
-
}
|
|
468
|
+
headers
|
|
442
469
|
});
|
|
443
470
|
const text = await response.text();
|
|
444
471
|
const body = text ? JSON.parse(text) : null;
|
|
@@ -449,6 +476,15 @@ function createReviewFigmaImageStoreRequest(endpoint, fetchOption) {
|
|
|
449
476
|
return body;
|
|
450
477
|
};
|
|
451
478
|
}
|
|
479
|
+
async function readReviewFigmaImageStoreHeaders(provider) {
|
|
480
|
+
return typeof provider === "function" ? provider() : provider;
|
|
481
|
+
}
|
|
482
|
+
function appendReviewFigmaImageStoreHeaders(target, source) {
|
|
483
|
+
if (!source) return;
|
|
484
|
+
new Headers(source).forEach((value, key) => {
|
|
485
|
+
target.set(key, value);
|
|
486
|
+
});
|
|
487
|
+
}
|
|
452
488
|
function readReviewFigmaImageToken(provider) {
|
|
453
489
|
const token = typeof provider === "function" ? provider() : provider;
|
|
454
490
|
return typeof token === "string" ? token.trim() : "";
|
|
@@ -593,10 +629,12 @@ export {
|
|
|
593
629
|
createReviewFigmaImageApiUrl,
|
|
594
630
|
DEFAULT_REVIEW_FIGMA_IMAGE_STORE_ENDPOINT,
|
|
595
631
|
createReviewFigmaImageStoreClient,
|
|
632
|
+
createEndpointReviewFigmaImageStore,
|
|
633
|
+
createReviewFigmaClientRenderedAsset,
|
|
596
634
|
getReviewFigmaImageTargetKey,
|
|
597
635
|
getReviewFigmaImageMimeType,
|
|
598
636
|
createReviewFigmaImagesSnapshot,
|
|
599
637
|
createReviewFigmaReleaseSnapshot,
|
|
600
638
|
collectReviewFigmaReleaseSnapshot
|
|
601
639
|
};
|
|
602
|
-
//# sourceMappingURL=chunk-
|
|
640
|
+
//# sourceMappingURL=chunk-UNDQZ4Y2.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/figma/parse.ts","../src/figma/token.ts","../src/figma/render.ts","../src/vite/figma-asset.ts","../src/figma/image.store.ts","../src/figma/image.snapshot.ts"],"sourcesContent":["export type ReviewFigmaNodeRef = {\n fileKey: string;\n nodeId: string;\n sourceUrl?: string;\n};\n\nexport const FIGMA_NODE_REF_SEPARATOR = '->';\n\nexport function parseReviewFigmaNodeRef(\n value: string | ReviewFigmaNodeRef\n): ReviewFigmaNodeRef | null {\n if (typeof value !== 'string') return normalizeReviewFigmaNodeRef(value);\n\n const input = value.trim();\n if (!input) return null;\n\n return parseReviewFigmaNodeRefValue(input) ?? parseReviewFigmaUrl(input);\n}\n\nexport function requireReviewFigmaNodeRef(\n value: string | ReviewFigmaNodeRef\n): ReviewFigmaNodeRef {\n const ref = parseReviewFigmaNodeRef(value);\n if (!ref) {\n throw new Error('A Figma node link or fileKey->nodeId value is required.');\n }\n return ref;\n}\n\nexport function createReviewFigmaNodeValue(ref: ReviewFigmaNodeRef) {\n return `${ref.fileKey}${FIGMA_NODE_REF_SEPARATOR}${ref.nodeId}`;\n}\n\nexport function createReviewFigmaFrameUrl(\n value: string | ReviewFigmaNodeRef\n) {\n const ref = parseReviewFigmaNodeRef(value);\n if (!ref) return null;\n\n return `https://www.figma.com/design/${encodeURIComponent(\n ref.fileKey\n )}?node-id=${encodeURIComponent(ref.nodeId.replace(/:/g, '-'))}`;\n}\n\nfunction parseReviewFigmaNodeRefValue(value: string) {\n const [fileKey, nodeId, extra] = value\n .split(FIGMA_NODE_REF_SEPARATOR)\n .map((part) => part.trim());\n\n if (!fileKey || !nodeId || extra !== undefined) return null;\n\n return normalizeReviewFigmaNodeRef({ fileKey, nodeId });\n}\n\nfunction parseReviewFigmaUrl(value: string) {\n let url: URL;\n try {\n url = new URL(value);\n } catch {\n return null;\n }\n\n const fileKey = getFigmaFileKey(url);\n const nodeId = normalizeFigmaNodeId(url.searchParams.get('node-id'));\n if (!fileKey || !nodeId) return null;\n\n return { fileKey, nodeId, sourceUrl: value };\n}\n\nfunction getFigmaFileKey(url: URL) {\n if (!/(^|\\.)figma\\.com$/i.test(url.hostname)) return null;\n\n const parts = url.pathname.split('/').filter(Boolean);\n const fileKeyIndex = parts.findIndex((part) =>\n ['design', 'file', 'proto', 'board'].includes(part)\n );\n const fileKey = fileKeyIndex >= 0 ? parts[fileKeyIndex + 1] : null;\n\n return normalizeFigmaFileKey(fileKey);\n}\n\nfunction normalizeReviewFigmaNodeRef(\n value: ReviewFigmaNodeRef | null | undefined\n) {\n const fileKey = normalizeFigmaFileKey(value?.fileKey);\n const nodeId = normalizeFigmaNodeId(value?.nodeId);\n if (!fileKey || !nodeId) return null;\n const sourceUrl = normalizeOptionalString(value?.sourceUrl);\n\n return sourceUrl ? { fileKey, nodeId, sourceUrl } : { fileKey, nodeId };\n}\n\nfunction normalizeFigmaFileKey(value: string | null | undefined) {\n return normalizeOptionalString(value)?.replace(/[?#].*$/, '') ?? null;\n}\n\nfunction normalizeFigmaNodeId(value: string | null | undefined) {\n const nodeId = normalizeOptionalString(value);\n if (!nodeId) return null;\n if (nodeId.includes(':')) return nodeId;\n return nodeId.replace(/-/g, ':');\n}\n\nfunction normalizeOptionalString(value: string | null | undefined) {\n if (typeof value !== 'string') return null;\n return value.trim() || null;\n}\n","export const DEFAULT_REVIEW_FIGMA_TOKEN_ENV_KEY = 'FIGMA_TOKEN';\nexport const REVIEW_FIGMA_TOKEN_MISSING_CODE = 'DFWR_FIGMA_TOKEN_MISSING';\n\nexport type ReviewFigmaTokenEnv = Record<\n string,\n string | null | undefined\n>;\n\nexport type ReviewFigmaTokenOptions = {\n token?: string | null;\n env?: ReviewFigmaTokenEnv;\n envKey?: string;\n enabled?: boolean;\n};\n\nexport class ReviewFigmaTokenError extends Error {\n readonly code = REVIEW_FIGMA_TOKEN_MISSING_CODE;\n readonly envKey: string;\n\n constructor(envKey = DEFAULT_REVIEW_FIGMA_TOKEN_ENV_KEY) {\n super(\n `Figma image rendering requires server env ${envKey}. Set ${envKey} in the dev/server environment; do not expose it as VITE_${envKey}.`\n );\n this.name = 'ReviewFigmaTokenError';\n this.envKey = envKey;\n }\n}\n\nexport function readReviewFigmaToken(\n options: ReviewFigmaTokenOptions = {}\n): string | null {\n if (options.enabled === false) return null;\n\n const envKey = options.envKey ?? DEFAULT_REVIEW_FIGMA_TOKEN_ENV_KEY;\n return normalizeReviewFigmaToken(options.token ?? options.env?.[envKey]);\n}\n\nexport function requireReviewFigmaToken(\n options: ReviewFigmaTokenOptions = {}\n): string {\n const envKey = options.envKey ?? DEFAULT_REVIEW_FIGMA_TOKEN_ENV_KEY;\n const token = readReviewFigmaToken(options);\n\n if (!token) throw new ReviewFigmaTokenError(envKey);\n return token;\n}\n\nexport function isReviewFigmaTokenError(\n error: unknown\n): error is ReviewFigmaTokenError {\n if (!error || typeof error !== 'object') return false;\n\n return (\n error instanceof ReviewFigmaTokenError ||\n ('code' in error && error.code === REVIEW_FIGMA_TOKEN_MISSING_CODE)\n );\n}\n\nfunction normalizeReviewFigmaToken(value: string | null | undefined) {\n if (typeof value !== 'string') return null;\n return value.trim() || null;\n}\n","import {\n requireReviewFigmaNodeRef,\n type ReviewFigmaNodeRef,\n} from './parse';\nimport { requireReviewFigmaToken } from './token';\n\nexport type ReviewFigmaRenderFormat = 'png' | 'jpg' | 'svg' | 'pdf';\n\nexport type ReviewFigmaImageRenderOptions = {\n figmaUrl: string | ReviewFigmaNodeRef;\n token?: string | null;\n format?: ReviewFigmaRenderFormat;\n scale?: number;\n useAbsoluteBounds?: boolean;\n apiBaseUrl?: string;\n fetch?: typeof fetch;\n signal?: AbortSignal;\n};\n\nexport type ReviewFigmaRenderedImage = {\n fileKey: string;\n nodeId: string;\n figmaUrl?: string;\n imageUrl: string;\n renderFormat: ReviewFigmaRenderFormat;\n};\n\ntype FigmaImageResponse = {\n err?: string | null;\n images?: Record<string, string | null | undefined>;\n};\n\nconst DEFAULT_FIGMA_API_BASE_URL = 'https://api.figma.com';\n\nexport async function renderReviewFigmaImage(\n options: ReviewFigmaImageRenderOptions\n): Promise<ReviewFigmaRenderedImage> {\n const token = requireReviewFigmaToken({ token: options.token });\n const ref = requireReviewFigmaNodeRef(options.figmaUrl);\n const renderFormat = options.format ?? 'png';\n const requestUrl = createReviewFigmaImageApiUrl({\n apiBaseUrl: options.apiBaseUrl,\n fileKey: ref.fileKey,\n nodeId: ref.nodeId,\n format: renderFormat,\n scale: options.scale,\n useAbsoluteBounds: options.useAbsoluteBounds,\n });\n const fetchImage = options.fetch ?? globalThis.fetch;\n if (!fetchImage) throw new Error('Figma image rendering requires fetch.');\n\n const response = await fetchImage(requestUrl, {\n headers: {\n 'X-Figma-Token': token,\n },\n signal: options.signal,\n });\n const body = (await response.json().catch(() => null)) as\n | FigmaImageResponse\n | null;\n\n if (!response.ok) {\n throw new Error(\n body?.err || `Figma image render failed with ${response.status}`\n );\n }\n\n const imageUrl = body?.images?.[ref.nodeId];\n if (!imageUrl) {\n throw new Error(`Figma image render returned no URL for ${ref.nodeId}.`);\n }\n\n return {\n fileKey: ref.fileKey,\n nodeId: ref.nodeId,\n figmaUrl: typeof options.figmaUrl === 'string'\n ? options.figmaUrl\n : options.figmaUrl.sourceUrl,\n imageUrl,\n renderFormat,\n };\n}\n\nexport function createReviewFigmaImageApiUrl({\n apiBaseUrl = DEFAULT_FIGMA_API_BASE_URL,\n fileKey,\n nodeId,\n format = 'png',\n scale,\n useAbsoluteBounds,\n}: {\n apiBaseUrl?: string;\n fileKey: string;\n nodeId: string;\n format?: ReviewFigmaRenderFormat;\n scale?: number;\n useAbsoluteBounds?: boolean;\n}) {\n const url = new URL(`/v1/images/${encodeURIComponent(fileKey)}`, apiBaseUrl);\n url.searchParams.set('ids', nodeId);\n url.searchParams.set('format', format);\n\n if (typeof scale === 'number' && Number.isFinite(scale) && scale > 0) {\n url.searchParams.set('scale', String(scale));\n }\n if (useAbsoluteBounds !== undefined) {\n url.searchParams.set('use_absolute_bounds', String(useAbsoluteBounds));\n }\n\n return url.toString();\n}\n","// Pure helpers for Figma image asset format conversion and storage-key handling.\n// Extracted from src/vite.ts to keep the plugin entry focused on request flow.\nimport type { ReviewFigmaImageFormat } from '../figma/image.types';\nimport type { ReviewFigmaRenderFormat } from '../figma/render';\n\nexport function parseReviewFigmaImageFormat(value: unknown) {\n return value === 'webp' || value === 'png' || value === 'jpg'\n ? value\n : undefined;\n}\n\nexport function getStoreRenderFormat(\n renderFormat: ReviewFigmaRenderFormat | undefined,\n imageFormat: ReviewFigmaImageFormat | undefined\n): Extract<ReviewFigmaRenderFormat, 'png' | 'jpg'> {\n if (renderFormat === 'jpg' || renderFormat === 'png') return renderFormat;\n if (imageFormat === 'jpg') return 'jpg';\n return 'png';\n}\n\nexport function getReviewFigmaImageFormatFromMimeType(\n mimeType: string\n): ReviewFigmaImageFormat | null {\n if (mimeType === 'image/webp') return 'webp';\n if (mimeType === 'image/png') return 'png';\n if (mimeType === 'image/jpeg') return 'jpg';\n return null;\n}\n\nexport function normalizeImageMimeType(value: string | null | undefined) {\n const mimeType = value?.split(';')[0]?.trim().toLowerCase();\n if (mimeType === 'image/jpg') return 'image/jpeg';\n if (\n mimeType === 'image/jpeg' ||\n mimeType === 'image/png' ||\n mimeType === 'image/webp'\n ) {\n return mimeType;\n }\n return null;\n}\n\nexport function createReviewFigmaAssetStorageKey(\n id: string,\n imageFormat: ReviewFigmaImageFormat\n) {\n return `${id}.${getReviewFigmaAssetExtension(imageFormat)}`;\n}\n\nexport function createReviewFigmaAssetUrl(\n assetEndpoint: string,\n storageKey: string\n) {\n return `${assetEndpoint}/${encodeURIComponent(storageKey)}`;\n}\n\nexport function getReviewFigmaAssetStorageKeyFromPathname(\n pathname: string,\n assetEndpoint: string\n) {\n try {\n const storageKey = decodeURIComponent(\n pathname.slice(assetEndpoint.length + 1)\n );\n return isSafeReviewFigmaAssetStorageKey(storageKey) ? storageKey : null;\n } catch {\n return null;\n }\n}\n\nexport function isSafeReviewFigmaAssetStorageKey(value: string) {\n return /^figma_[a-z0-9_]+\\.(webp|png|jpg)$/.test(value);\n}\n\nfunction getReviewFigmaAssetExtension(format: ReviewFigmaImageFormat) {\n return format === 'jpg' ? 'jpg' : format;\n}\n\nexport function getReviewFigmaAssetMimeType(storageKey: string) {\n if (storageKey.endsWith('.jpg')) return 'image/jpeg';\n if (storageKey.endsWith('.webp')) return 'image/webp';\n return 'image/png';\n}\n","import type {\n AddReviewFigmaImageInput,\n ReorderReviewFigmaImagesInput,\n ReviewFigmaImage,\n ReviewFigmaImageAssetInput,\n ReviewFigmaImageFormat,\n ReviewFigmaImageStore,\n ReviewFigmaImageTarget,\n} from './image.types';\nimport { getReviewFigmaImageFormatFromMimeType } from '../vite/figma-asset';\nimport { parseReviewFigmaNodeRef } from './parse';\nimport {\n createReviewFigmaImageApiUrl,\n type ReviewFigmaRenderFormat,\n} from './render';\n\nexport const DEFAULT_REVIEW_FIGMA_IMAGE_STORE_ENDPOINT =\n '/__dfwr/figma-images';\n\ntype ReviewFigmaImageTokenProvider =\n | string\n | null\n | undefined\n | (() => string | null | undefined);\n\nexport type ReviewFigmaImageStoreHeadersProvider =\n | HeadersInit\n | null\n | undefined\n | (() => HeadersInit | null | undefined | Promise<HeadersInit | null | undefined>);\n\nexport type ReviewFigmaImageStoreClientOptions = {\n endpoint?: string;\n fetch?: typeof fetch;\n token?: ReviewFigmaImageTokenProvider;\n clientRender?: boolean | ReviewFigmaImageClientRenderOptions;\n};\n\nexport type EndpointReviewFigmaImageStoreOptions =\n ReviewFigmaImageStoreClientOptions & {\n headers?: ReviewFigmaImageStoreHeadersProvider;\n };\n\nexport type ReviewFigmaImageClientRenderOptions = {\n token?: string | null | (() => string | null | undefined);\n apiBaseUrl?: string;\n renderFormat?: Extract<ReviewFigmaRenderFormat, 'png' | 'jpg'>;\n renderScale?: number;\n useAbsoluteBounds?: boolean;\n convertToWebp?: boolean;\n webpQuality?: number;\n timeoutMs?: number;\n};\n\nexport type CreateReviewFigmaClientRenderedAssetOptions =\n ReviewFigmaImageClientRenderOptions & {\n figmaUrl: string;\n token: string;\n fetch?: typeof fetch;\n };\n\nexport function createReviewFigmaImageStoreClient(\n options: ReviewFigmaImageStoreClientOptions = {}\n): ReviewFigmaImageStore {\n return createEndpointReviewFigmaImageStore(options);\n}\n\nexport function createEndpointReviewFigmaImageStore(\n options: EndpointReviewFigmaImageStoreOptions = {}\n): ReviewFigmaImageStore {\n const endpoint =\n options.endpoint ?? DEFAULT_REVIEW_FIGMA_IMAGE_STORE_ENDPOINT;\n const request = createReviewFigmaImageStoreRequest(\n options.fetch,\n options.headers\n );\n\n return {\n listImages(target) {\n const url = `${endpoint}?target=${encodeURIComponent(\n JSON.stringify(target)\n )}`;\n return request<ReviewFigmaImage[]>(url);\n },\n async addImage(input) {\n const nextInput = await createClientRenderedAddImageInput(\n input,\n options.clientRender,\n options.fetch\n );\n return request<ReviewFigmaImage>(endpoint, {\n method: 'POST',\n body: JSON.stringify(nextInput),\n figmaToken: readReviewFigmaImageToken(\n options.token ?? getStoredReviewFigmaImageToken\n ),\n });\n },\n updateImage(id, patch) {\n return request<ReviewFigmaImage>(`${endpoint}/${encodeURIComponent(id)}`, {\n method: 'PATCH',\n body: JSON.stringify(patch),\n });\n },\n reorderImages(input) {\n return request<ReviewFigmaImage[]>(`${endpoint}/reorder`, {\n method: 'PATCH',\n body: JSON.stringify(input),\n });\n },\n deleteImage(id) {\n return request<void>(`${endpoint}/${encodeURIComponent(id)}`, {\n method: 'DELETE',\n });\n },\n };\n}\n\nasync function createClientRenderedAddImageInput(\n input: AddReviewFigmaImageInput,\n clientRender: ReviewFigmaImageStoreClientOptions['clientRender'],\n fetchOption: typeof fetch | undefined\n): Promise<AddReviewFigmaImageInput> {\n const options = normalizeClientRenderOptions(clientRender);\n if (!options) return input;\n\n const token = readClientRenderToken(options);\n if (!token) return input;\n\n try {\n const asset = await createReviewFigmaClientRenderedAsset({\n ...options,\n fetch: fetchOption,\n figmaUrl: input.figmaUrl,\n token,\n });\n return {\n ...input,\n imageFormat: asset.imageFormat,\n asset,\n };\n } catch {\n return input;\n }\n}\n\nfunction normalizeClientRenderOptions(\n clientRender: ReviewFigmaImageStoreClientOptions['clientRender']\n): ReviewFigmaImageClientRenderOptions | null {\n if (!clientRender) return null;\n if (clientRender === true) return {};\n return clientRender;\n}\n\nfunction readClientRenderToken(options: ReviewFigmaImageClientRenderOptions) {\n const token =\n typeof options.token === 'function' ? options.token() : options.token;\n return typeof token === 'string' ? token.trim() : '';\n}\n\nasync function createClientRenderedFigmaAsset(\n figmaUrl: string,\n token: string,\n options: ReviewFigmaImageClientRenderOptions,\n fetchOption: typeof fetch | undefined\n): Promise<ReviewFigmaImageAssetInput> {\n const ref = parseReviewFigmaNodeRef(figmaUrl);\n if (!ref) throw new Error('A Figma node link is required.');\n\n const requestFetch = fetchOption ?? globalThis.fetch;\n if (!requestFetch) throw new Error('Figma client rendering requires fetch.');\n\n const renderFormat = options.renderFormat ?? 'png';\n const response = await requestFetch(\n createReviewFigmaImageApiUrl({\n apiBaseUrl: options.apiBaseUrl,\n fileKey: ref.fileKey,\n nodeId: ref.nodeId,\n format: renderFormat,\n scale: options.renderScale,\n useAbsoluteBounds: options.useAbsoluteBounds,\n }),\n {\n headers: {\n 'X-Figma-Token': token,\n },\n }\n );\n const body = (await response.json().catch(() => null)) as\n | { err?: string | null; images?: Record<string, string | null | undefined> }\n | null;\n if (!response.ok) {\n throw new Error(body?.err || `Figma image render failed with ${response.status}`);\n }\n\n const imageUrl = body?.images?.[ref.nodeId];\n if (!imageUrl) throw new Error(`Figma image render returned no URL for ${ref.nodeId}.`);\n\n const imageResponse = await requestFetch(imageUrl);\n if (!imageResponse.ok) {\n throw new Error(`Figma image download failed with ${imageResponse.status}`);\n }\n\n const originalBlob = await imageResponse.blob();\n const originalMimeType =\n normalizeClientImageMimeType(originalBlob.type) ??\n getReviewFigmaImageMimeType(renderFormat === 'jpg' ? 'jpg' : 'png');\n const originalFormat =\n getReviewFigmaImageFormatFromMimeType(originalMimeType) ??\n (renderFormat === 'jpg' ? 'jpg' : 'png');\n const dimensions = await readImageBlobDimensions(originalBlob);\n const shouldConvertToWebp = options.convertToWebp ?? true;\n const convertedBlob = shouldConvertToWebp\n ? await convertImageBlobToWebp(\n originalBlob,\n options.webpQuality ?? 0.9,\n dimensions\n ).catch(() => null)\n : null;\n const finalBlob =\n convertedBlob?.type === 'image/webp' ? convertedBlob : originalBlob;\n const finalMimeType =\n normalizeClientImageMimeType(finalBlob.type) ?? originalMimeType;\n const finalFormat =\n getReviewFigmaImageFormatFromMimeType(finalMimeType) ?? originalFormat;\n\n return {\n dataUrl: await blobToDataUrl(finalBlob),\n imageFormat: finalFormat,\n mimeType: finalMimeType,\n byteSize: finalBlob.size,\n width: dimensions.width,\n height: dimensions.height,\n };\n}\n\nexport function createReviewFigmaClientRenderedAsset({\n fetch: fetchOption,\n figmaUrl,\n token,\n ...options\n}: CreateReviewFigmaClientRenderedAssetOptions): Promise<ReviewFigmaImageAssetInput> {\n return withTimeout(\n createClientRenderedFigmaAsset(figmaUrl, token, options, fetchOption),\n options.timeoutMs ?? 10000\n );\n}\n\nasync function readImageBlobDimensions(blob: Blob) {\n const image = await loadImageBlob(blob);\n return {\n width: image.naturalWidth || image.width,\n height: image.naturalHeight || image.height,\n };\n}\n\nasync function convertImageBlobToWebp(\n blob: Blob,\n quality: number,\n dimensions: { width: number; height: number }\n) {\n if (typeof document === 'undefined') return null;\n if (!dimensions.width || !dimensions.height) return null;\n\n const image = await loadImageBlob(blob);\n const canvas = document.createElement('canvas');\n canvas.width = dimensions.width;\n canvas.height = dimensions.height;\n const context = canvas.getContext('2d');\n if (!context) return null;\n\n context.drawImage(image, 0, 0);\n\n return new Promise<Blob | null>((resolve) => {\n canvas.toBlob(resolve, 'image/webp', quality);\n });\n}\n\nfunction loadImageBlob(blob: Blob) {\n return new Promise<HTMLImageElement>((resolve, reject) => {\n if (typeof Image === 'undefined' || typeof URL === 'undefined') {\n reject(new Error('Image decoding is unavailable.'));\n return;\n }\n\n const image = new Image();\n const objectUrl = URL.createObjectURL(blob);\n image.onload = () => {\n URL.revokeObjectURL(objectUrl);\n resolve(image);\n };\n image.onerror = () => {\n URL.revokeObjectURL(objectUrl);\n reject(new Error('Image decoding failed.'));\n };\n image.src = objectUrl;\n });\n}\n\nfunction blobToDataUrl(blob: Blob) {\n return new Promise<string>((resolve, reject) => {\n const reader = new FileReader();\n reader.onload = () => {\n if (typeof reader.result === 'string') {\n resolve(reader.result);\n return;\n }\n reject(new Error('Blob encoding failed.'));\n };\n reader.onerror = () => reject(reader.error ?? new Error('Blob encoding failed.'));\n reader.readAsDataURL(blob);\n });\n}\n\nfunction normalizeClientImageMimeType(value: string | null | undefined) {\n const mimeType = value?.split(';')[0]?.trim().toLowerCase();\n if (mimeType === 'image/jpg') return 'image/jpeg';\n if (\n mimeType === 'image/jpeg' ||\n mimeType === 'image/png' ||\n mimeType === 'image/webp'\n ) {\n return mimeType;\n }\n return null;\n}\n\nasync function withTimeout<T>(promise: Promise<T>, timeoutMs: number) {\n let timeoutId: ReturnType<typeof setTimeout> | undefined;\n try {\n return await Promise.race([\n promise,\n new Promise<T>((_, reject) => {\n timeoutId = setTimeout(\n () => reject(new Error('Figma client rendering timed out.')),\n timeoutMs\n );\n }),\n ]);\n } finally {\n if (timeoutId) clearTimeout(timeoutId);\n }\n}\n\nexport function getReviewFigmaImageTargetKey(target: ReviewFigmaImageTarget) {\n return JSON.stringify(normalizeReviewFigmaImageTarget(target));\n}\n\nexport function getReviewFigmaImageMimeType(\n format: ReviewFigmaImageFormat\n) {\n if (format === 'jpg') return 'image/jpeg';\n if (format === 'png') return 'image/png';\n return 'image/webp';\n}\n\ntype ReviewFigmaImageStoreRequestInit = RequestInit & {\n figmaToken?: string;\n};\n\nfunction createReviewFigmaImageStoreRequest(\n fetchOption: typeof fetch | undefined,\n headersProvider: ReviewFigmaImageStoreHeadersProvider\n) {\n return async <T>(\n input: string,\n init: ReviewFigmaImageStoreRequestInit = {}\n ) => {\n const requestFetch = fetchOption ?? globalThis.fetch;\n if (!requestFetch) throw new Error('Figma image store requires fetch.');\n const figmaToken = init.figmaToken ?? '';\n const {\n figmaToken: _figmaToken,\n headers: requestHeaders,\n ...requestInit\n } = init;\n const headers = new Headers();\n headers.set('Content-Type', 'application/json');\n if (figmaToken) headers.set('X-Figma-Token', figmaToken);\n appendReviewFigmaImageStoreHeaders(\n headers,\n await readReviewFigmaImageStoreHeaders(headersProvider)\n );\n appendReviewFigmaImageStoreHeaders(headers, requestHeaders);\n\n const response = await requestFetch(input, {\n ...requestInit,\n headers,\n });\n const text = await response.text();\n const body = text ? JSON.parse(text) : null;\n\n if (!response.ok) {\n const message =\n typeof body?.error === 'string'\n ? body.error\n : `Figma image store request failed: ${response.status}`;\n throw new Error(message);\n }\n\n return body as T;\n };\n}\n\nasync function readReviewFigmaImageStoreHeaders(\n provider: ReviewFigmaImageStoreHeadersProvider\n) {\n return typeof provider === 'function' ? provider() : provider;\n}\n\nfunction appendReviewFigmaImageStoreHeaders(\n target: Headers,\n source: HeadersInit | null | undefined\n) {\n if (!source) return;\n new Headers(source).forEach((value, key) => {\n target.set(key, value);\n });\n}\n\nfunction readReviewFigmaImageToken(provider: ReviewFigmaImageTokenProvider) {\n const token = typeof provider === 'function' ? provider() : provider;\n return typeof token === 'string' ? token.trim() : '';\n}\n\nfunction getStoredReviewFigmaImageToken() {\n if (typeof window === 'undefined') return '';\n\n try {\n return window.localStorage.getItem('figma-token') ?? '';\n } catch {\n return '';\n }\n}\n\nfunction normalizeReviewFigmaImageTarget(target: ReviewFigmaImageTarget) {\n if (target.type === 'figma-node') {\n return {\n type: target.type,\n projectId: target.projectId,\n fileKey: target.fileKey,\n nodeId: target.nodeId,\n };\n }\n\n return {\n type: target.type,\n projectId: target.projectId,\n pageUrl: target.pageUrl,\n slot: target.slot ?? '',\n viewport: target.viewport\n ? {\n label: target.viewport.label ?? '',\n width: target.viewport.width ?? null,\n height: target.viewport.height ?? null,\n scope: target.viewport.scope ?? '',\n }\n : null,\n };\n}\n\nexport type { ReorderReviewFigmaImagesInput };\n","import type {\n ReviewFigmaImage,\n ReviewFigmaImageStore,\n ReviewFigmaImageTarget,\n} from './image.types';\nimport { getReviewFigmaImageTargetKey } from './image.store';\n\nexport type ReviewFigmaImagesSnapshot = ReviewFigmaImage[];\n\nexport type ReviewFigmaReleaseSnapshot = {\n version: 1;\n projectId: string;\n releaseId?: string;\n label?: string;\n createdAt: string;\n figmaImagesSnapshot: ReviewFigmaImagesSnapshot;\n};\n\nexport type CreateReviewFigmaImagesSnapshotOptions = {\n projectId?: string;\n targets?: readonly ReviewFigmaImageTarget[];\n};\n\nexport type CreateReviewFigmaReleaseSnapshotOptions =\n CreateReviewFigmaImagesSnapshotOptions & {\n images: readonly ReviewFigmaImage[];\n projectId: string;\n releaseId?: string;\n label?: string;\n createdAt?: string;\n };\n\nexport type CollectReviewFigmaReleaseSnapshotOptions = Omit<\n CreateReviewFigmaReleaseSnapshotOptions,\n 'images'\n> & {\n store: ReviewFigmaImageStore;\n targets: readonly ReviewFigmaImageTarget[];\n};\n\nexport function createReviewFigmaImagesSnapshot(\n images: readonly ReviewFigmaImage[],\n options: CreateReviewFigmaImagesSnapshotOptions = {}\n): ReviewFigmaImagesSnapshot {\n const targetKeys = options.targets?.length\n ? new Set(options.targets.map(getReviewFigmaImageTargetKey))\n : null;\n\n return images\n .filter((image) => {\n if (options.projectId && image.projectId !== options.projectId) {\n return false;\n }\n if (\n targetKeys &&\n !targetKeys.has(getReviewFigmaImageTargetKey(image.target))\n ) {\n return false;\n }\n return true;\n })\n .map(cloneReviewFigmaImage)\n .sort(compareReviewFigmaSnapshotImages);\n}\n\nexport function createReviewFigmaReleaseSnapshot({\n images,\n projectId,\n releaseId,\n label,\n createdAt,\n targets,\n}: CreateReviewFigmaReleaseSnapshotOptions): ReviewFigmaReleaseSnapshot {\n return {\n version: 1,\n projectId,\n ...(releaseId ? { releaseId } : null),\n ...(label ? { label } : null),\n createdAt: createdAt ?? new Date().toISOString(),\n figmaImagesSnapshot: createReviewFigmaImagesSnapshot(images, {\n projectId,\n targets,\n }),\n };\n}\n\nexport async function collectReviewFigmaReleaseSnapshot({\n store,\n targets,\n ...snapshotOptions\n}: CollectReviewFigmaReleaseSnapshotOptions): Promise<ReviewFigmaReleaseSnapshot> {\n const imagesByTarget = await Promise.all(\n targets.map((target) => store.listImages(target))\n );\n\n return createReviewFigmaReleaseSnapshot({\n ...snapshotOptions,\n targets,\n images: dedupeReviewFigmaImages(imagesByTarget.flat()),\n });\n}\n\nfunction dedupeReviewFigmaImages(images: readonly ReviewFigmaImage[]) {\n return Array.from(new Map(images.map((image) => [image.id, image])).values());\n}\n\nfunction cloneReviewFigmaImage(image: ReviewFigmaImage): ReviewFigmaImage {\n return {\n ...image,\n target: cloneReviewFigmaImageTarget(image.target),\n };\n}\n\nfunction cloneReviewFigmaImageTarget(\n target: ReviewFigmaImageTarget\n): ReviewFigmaImageTarget {\n if (target.type === 'figma-node') {\n return {\n type: target.type,\n projectId: target.projectId,\n fileKey: target.fileKey,\n nodeId: target.nodeId,\n };\n }\n\n return {\n type: target.type,\n projectId: target.projectId,\n pageUrl: target.pageUrl,\n slot: target.slot,\n viewport: target.viewport\n ? {\n label: target.viewport.label,\n width: target.viewport.width,\n height: target.viewport.height,\n scope: target.viewport.scope,\n }\n : undefined,\n };\n}\n\nfunction compareReviewFigmaSnapshotImages(\n a: ReviewFigmaImage,\n b: ReviewFigmaImage\n) {\n return (\n a.projectId.localeCompare(b.projectId) ||\n getReviewFigmaImageTargetKey(a.target).localeCompare(\n getReviewFigmaImageTargetKey(b.target)\n ) ||\n a.order - b.order ||\n a.createdAt.localeCompare(b.createdAt) ||\n a.id.localeCompare(b.id)\n );\n}\n"],"mappings":"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","names":[]}
|
package/dist/index.cjs
CHANGED
|
@@ -22,6 +22,7 @@ var index_exports = {};
|
|
|
22
22
|
__export(index_exports, {
|
|
23
23
|
DEFAULT_REVIEW_FIGMA_IMAGE_FORMAT: () => DEFAULT_REVIEW_FIGMA_IMAGE_FORMAT,
|
|
24
24
|
DEFAULT_REVIEW_FIGMA_IMAGE_STORE_ENDPOINT: () => DEFAULT_REVIEW_FIGMA_IMAGE_STORE_ENDPOINT,
|
|
25
|
+
DEFAULT_REVIEW_FIGMA_REMOTE_IMAGES_TABLE: () => DEFAULT_REVIEW_FIGMA_REMOTE_IMAGES_TABLE,
|
|
25
26
|
DEFAULT_REVIEW_FIGMA_TOKEN_ENV_KEY: () => DEFAULT_REVIEW_FIGMA_TOKEN_ENV_KEY,
|
|
26
27
|
DEFAULT_REVIEW_VIEWPORTS: () => DEFAULT_REVIEW_VIEWPORTS,
|
|
27
28
|
FIGMA_NODE_REF_SEPARATOR: () => FIGMA_NODE_REF_SEPARATOR,
|
|
@@ -29,6 +30,8 @@ __export(index_exports, {
|
|
|
29
30
|
REVIEW_WORKFLOW_STATUS_OPTIONS: () => REVIEW_WORKFLOW_STATUS_OPTIONS,
|
|
30
31
|
ReviewFigmaTokenError: () => ReviewFigmaTokenError,
|
|
31
32
|
collectReviewFigmaReleaseSnapshot: () => collectReviewFigmaReleaseSnapshot,
|
|
33
|
+
createEndpointReviewFigmaImageStore: () => createEndpointReviewFigmaImageStore,
|
|
34
|
+
createRemoteReviewFigmaImageStore: () => createRemoteReviewFigmaImageStore,
|
|
32
35
|
createReviewFigmaFrameUrl: () => createReviewFigmaFrameUrl,
|
|
33
36
|
createReviewFigmaImageStoreClient: () => createReviewFigmaImageStoreClient,
|
|
34
37
|
createReviewFigmaImagesSnapshot: () => createReviewFigmaImagesSnapshot,
|
|
@@ -532,8 +535,14 @@ function createReviewFigmaImageApiUrl({
|
|
|
532
535
|
// src/figma/image.store.ts
|
|
533
536
|
var DEFAULT_REVIEW_FIGMA_IMAGE_STORE_ENDPOINT = "/__dfwr/figma-images";
|
|
534
537
|
function createReviewFigmaImageStoreClient(options = {}) {
|
|
538
|
+
return createEndpointReviewFigmaImageStore(options);
|
|
539
|
+
}
|
|
540
|
+
function createEndpointReviewFigmaImageStore(options = {}) {
|
|
535
541
|
const endpoint = options.endpoint ?? DEFAULT_REVIEW_FIGMA_IMAGE_STORE_ENDPOINT;
|
|
536
|
-
const request = createReviewFigmaImageStoreRequest(
|
|
542
|
+
const request = createReviewFigmaImageStoreRequest(
|
|
543
|
+
options.fetch,
|
|
544
|
+
options.headers
|
|
545
|
+
);
|
|
537
546
|
return {
|
|
538
547
|
listImages(target) {
|
|
539
548
|
const url = `${endpoint}?target=${encodeURIComponent(
|
|
@@ -580,10 +589,12 @@ async function createClientRenderedAddImageInput(input, clientRender, fetchOptio
|
|
|
580
589
|
const token = readClientRenderToken(options);
|
|
581
590
|
if (!token) return input;
|
|
582
591
|
try {
|
|
583
|
-
const asset = await
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
592
|
+
const asset = await createReviewFigmaClientRenderedAsset({
|
|
593
|
+
...options,
|
|
594
|
+
fetch: fetchOption,
|
|
595
|
+
figmaUrl: input.figmaUrl,
|
|
596
|
+
token
|
|
597
|
+
});
|
|
587
598
|
return {
|
|
588
599
|
...input,
|
|
589
600
|
imageFormat: asset.imageFormat,
|
|
@@ -655,6 +666,17 @@ async function createClientRenderedFigmaAsset(figmaUrl, token, options, fetchOpt
|
|
|
655
666
|
height: dimensions.height
|
|
656
667
|
};
|
|
657
668
|
}
|
|
669
|
+
function createReviewFigmaClientRenderedAsset({
|
|
670
|
+
fetch: fetchOption,
|
|
671
|
+
figmaUrl,
|
|
672
|
+
token,
|
|
673
|
+
...options
|
|
674
|
+
}) {
|
|
675
|
+
return withTimeout(
|
|
676
|
+
createClientRenderedFigmaAsset(figmaUrl, token, options, fetchOption),
|
|
677
|
+
options.timeoutMs ?? 1e4
|
|
678
|
+
);
|
|
679
|
+
}
|
|
658
680
|
async function readImageBlobDimensions(blob) {
|
|
659
681
|
const image = await loadImageBlob(blob);
|
|
660
682
|
return {
|
|
@@ -741,19 +763,27 @@ function getReviewFigmaImageMimeType(format) {
|
|
|
741
763
|
if (format === "png") return "image/png";
|
|
742
764
|
return "image/webp";
|
|
743
765
|
}
|
|
744
|
-
function createReviewFigmaImageStoreRequest(
|
|
766
|
+
function createReviewFigmaImageStoreRequest(fetchOption, headersProvider) {
|
|
745
767
|
return async (input, init = {}) => {
|
|
746
768
|
const requestFetch = fetchOption ?? globalThis.fetch;
|
|
747
769
|
if (!requestFetch) throw new Error("Figma image store requires fetch.");
|
|
748
770
|
const figmaToken = init.figmaToken ?? "";
|
|
749
|
-
const {
|
|
771
|
+
const {
|
|
772
|
+
figmaToken: _figmaToken,
|
|
773
|
+
headers: requestHeaders,
|
|
774
|
+
...requestInit
|
|
775
|
+
} = init;
|
|
776
|
+
const headers = new Headers();
|
|
777
|
+
headers.set("Content-Type", "application/json");
|
|
778
|
+
if (figmaToken) headers.set("X-Figma-Token", figmaToken);
|
|
779
|
+
appendReviewFigmaImageStoreHeaders(
|
|
780
|
+
headers,
|
|
781
|
+
await readReviewFigmaImageStoreHeaders(headersProvider)
|
|
782
|
+
);
|
|
783
|
+
appendReviewFigmaImageStoreHeaders(headers, requestHeaders);
|
|
750
784
|
const response = await requestFetch(input, {
|
|
751
785
|
...requestInit,
|
|
752
|
-
headers
|
|
753
|
-
"Content-Type": "application/json",
|
|
754
|
-
...figmaToken ? { "X-Figma-Token": figmaToken } : {},
|
|
755
|
-
...requestInit.headers ?? {}
|
|
756
|
-
}
|
|
786
|
+
headers
|
|
757
787
|
});
|
|
758
788
|
const text = await response.text();
|
|
759
789
|
const body = text ? JSON.parse(text) : null;
|
|
@@ -764,6 +794,15 @@ function createReviewFigmaImageStoreRequest(endpoint, fetchOption) {
|
|
|
764
794
|
return body;
|
|
765
795
|
};
|
|
766
796
|
}
|
|
797
|
+
async function readReviewFigmaImageStoreHeaders(provider) {
|
|
798
|
+
return typeof provider === "function" ? provider() : provider;
|
|
799
|
+
}
|
|
800
|
+
function appendReviewFigmaImageStoreHeaders(target, source) {
|
|
801
|
+
if (!source) return;
|
|
802
|
+
new Headers(source).forEach((value, key) => {
|
|
803
|
+
target.set(key, value);
|
|
804
|
+
});
|
|
805
|
+
}
|
|
767
806
|
function readReviewFigmaImageToken(provider) {
|
|
768
807
|
const token = typeof provider === "function" ? provider() : provider;
|
|
769
808
|
return typeof token === "string" ? token.trim() : "";
|
|
@@ -799,6 +838,300 @@ function normalizeReviewFigmaImageTarget(target) {
|
|
|
799
838
|
};
|
|
800
839
|
}
|
|
801
840
|
|
|
841
|
+
// src/figma/remote.image.store.ts
|
|
842
|
+
var DEFAULT_REVIEW_FIGMA_REMOTE_IMAGES_TABLE = "review_figma_images";
|
|
843
|
+
var DEFAULT_REVIEW_FIGMA_REMOTE_UPLOAD_TIMEOUT_MS = 3e4;
|
|
844
|
+
var FIGMA_TOKEN_STORAGE_KEY = "figma-token";
|
|
845
|
+
function createRemoteReviewFigmaImageStore({
|
|
846
|
+
client,
|
|
847
|
+
uploadEndpoint,
|
|
848
|
+
table = DEFAULT_REVIEW_FIGMA_REMOTE_IMAGES_TABLE,
|
|
849
|
+
fetch: fetchOption,
|
|
850
|
+
token,
|
|
851
|
+
clientRender = true,
|
|
852
|
+
uploadTimeoutMs = DEFAULT_REVIEW_FIGMA_REMOTE_UPLOAD_TIMEOUT_MS
|
|
853
|
+
}) {
|
|
854
|
+
const listImages = async (target) => {
|
|
855
|
+
const targetKey = getReviewFigmaImageTargetKey(target);
|
|
856
|
+
const { data, error } = await client.from(table).select("*").eq("project_id", target.projectId).eq("target_key", targetKey).order("sort_order", { ascending: true }).order("created_at", { ascending: true });
|
|
857
|
+
if (error) throwRemoteStoreError(error, "list Figma images");
|
|
858
|
+
return (data ?? []).map(rowToFigmaImage);
|
|
859
|
+
};
|
|
860
|
+
return {
|
|
861
|
+
listImages,
|
|
862
|
+
async addImage(input) {
|
|
863
|
+
const ref = parseReviewFigmaNodeRef(input.figmaUrl);
|
|
864
|
+
if (!ref) {
|
|
865
|
+
throw new Error("A Figma node link or fileKey->nodeId value is required.");
|
|
866
|
+
}
|
|
867
|
+
const id = createFigmaImageId();
|
|
868
|
+
const asset = await resolveFigmaImageAsset({
|
|
869
|
+
input,
|
|
870
|
+
token,
|
|
871
|
+
clientRender,
|
|
872
|
+
fetch: fetchOption
|
|
873
|
+
});
|
|
874
|
+
const uploaded = await uploadFigmaAsset({
|
|
875
|
+
asset,
|
|
876
|
+
endpoint: uploadEndpoint,
|
|
877
|
+
fetch: fetchOption,
|
|
878
|
+
imageId: id,
|
|
879
|
+
projectId: input.target.projectId,
|
|
880
|
+
timeoutMs: uploadTimeoutMs
|
|
881
|
+
});
|
|
882
|
+
const order = typeof input.order === "number" && Number.isFinite(input.order) ? input.order : await getNextImageOrder(listImages, input.target);
|
|
883
|
+
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
884
|
+
const row = createImageRow({
|
|
885
|
+
asset,
|
|
886
|
+
fileKey: ref.fileKey,
|
|
887
|
+
id,
|
|
888
|
+
imageUrl: uploaded.imageUrl,
|
|
889
|
+
input,
|
|
890
|
+
nodeId: ref.nodeId,
|
|
891
|
+
now,
|
|
892
|
+
order,
|
|
893
|
+
storageKey: uploaded.storageKey
|
|
894
|
+
});
|
|
895
|
+
const { data, error } = await client.from(table).insert(row).select("*").single();
|
|
896
|
+
if (error) throwRemoteStoreError(error, "insert Figma image");
|
|
897
|
+
return rowToFigmaImage(data);
|
|
898
|
+
},
|
|
899
|
+
async updateImage(id, patch) {
|
|
900
|
+
const { data, error } = await client.from(table).update(createUpdatePatch(patch)).eq("id", id).select("*").single();
|
|
901
|
+
if (error) throwRemoteStoreError(error, "update Figma image");
|
|
902
|
+
return rowToFigmaImage(data);
|
|
903
|
+
},
|
|
904
|
+
async reorderImages(input) {
|
|
905
|
+
const current = await listImages(input.target);
|
|
906
|
+
const currentIds = new Set(current.map((image) => image.id));
|
|
907
|
+
const nextIds = [
|
|
908
|
+
...input.imageIds.filter((id) => currentIds.has(id)),
|
|
909
|
+
...current.map((image) => image.id).filter((id) => !input.imageIds.includes(id))
|
|
910
|
+
];
|
|
911
|
+
const targetKey = getReviewFigmaImageTargetKey(input.target);
|
|
912
|
+
const updatedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
913
|
+
const results = await Promise.all(
|
|
914
|
+
nextIds.map(
|
|
915
|
+
(id, index) => client.from(table).update({ sort_order: index, updated_at: updatedAt }).eq("id", id).eq("project_id", input.target.projectId).eq("target_key", targetKey)
|
|
916
|
+
)
|
|
917
|
+
);
|
|
918
|
+
const failed = results.find((result) => result.error);
|
|
919
|
+
if (failed?.error) throwRemoteStoreError(failed.error, "reorder Figma images");
|
|
920
|
+
return listImages(input.target);
|
|
921
|
+
},
|
|
922
|
+
async deleteImage(id) {
|
|
923
|
+
const { error } = await client.from(table).delete().eq("id", id);
|
|
924
|
+
if (error) throwRemoteStoreError(error, "delete Figma image");
|
|
925
|
+
}
|
|
926
|
+
};
|
|
927
|
+
}
|
|
928
|
+
async function resolveFigmaImageAsset({
|
|
929
|
+
input,
|
|
930
|
+
token,
|
|
931
|
+
clientRender,
|
|
932
|
+
fetch
|
|
933
|
+
}) {
|
|
934
|
+
if (input.asset) return input.asset;
|
|
935
|
+
if (!clientRender) {
|
|
936
|
+
throw new Error("Figma image asset is required when clientRender is disabled.");
|
|
937
|
+
}
|
|
938
|
+
const renderOptions = clientRender === true ? {} : clientRender;
|
|
939
|
+
const figmaToken = readRemoteFigmaToken(renderOptions.token ?? token);
|
|
940
|
+
if (!figmaToken) throw new Error("Figma token is required.");
|
|
941
|
+
return createReviewFigmaClientRenderedAsset({
|
|
942
|
+
...renderOptions,
|
|
943
|
+
fetch,
|
|
944
|
+
figmaUrl: input.figmaUrl,
|
|
945
|
+
token: figmaToken
|
|
946
|
+
});
|
|
947
|
+
}
|
|
948
|
+
async function uploadFigmaAsset({
|
|
949
|
+
asset,
|
|
950
|
+
endpoint,
|
|
951
|
+
fetch: fetchOption,
|
|
952
|
+
imageId,
|
|
953
|
+
projectId,
|
|
954
|
+
timeoutMs
|
|
955
|
+
}) {
|
|
956
|
+
const requestFetch = fetchOption ?? globalThis.fetch;
|
|
957
|
+
if (!requestFetch) throw new Error("Figma asset upload requires fetch.");
|
|
958
|
+
const { blob, mimeType } = decodeAssetDataUrl(asset);
|
|
959
|
+
const response = await withTimeout2(
|
|
960
|
+
requestFetch(
|
|
961
|
+
createAssetUploadUrl(endpoint, {
|
|
962
|
+
imageFormat: asset.imageFormat,
|
|
963
|
+
imageId,
|
|
964
|
+
projectId
|
|
965
|
+
}),
|
|
966
|
+
{
|
|
967
|
+
method: "POST",
|
|
968
|
+
headers: {
|
|
969
|
+
"Content-Type": mimeType
|
|
970
|
+
},
|
|
971
|
+
body: blob
|
|
972
|
+
}
|
|
973
|
+
),
|
|
974
|
+
timeoutMs,
|
|
975
|
+
"Figma asset upload timed out."
|
|
976
|
+
);
|
|
977
|
+
const data = await response.json().catch(() => null);
|
|
978
|
+
if (!response.ok) {
|
|
979
|
+
throw new Error(
|
|
980
|
+
readString(data?.error) || `Figma asset upload failed with ${response.status}`
|
|
981
|
+
);
|
|
982
|
+
}
|
|
983
|
+
const storageKey = readString(data?.storageKey) ?? readString(data?.r2Key);
|
|
984
|
+
const imageUrl = readString(data?.imageUrl) ?? readString(data?.publicUrl);
|
|
985
|
+
if (!storageKey || !imageUrl) {
|
|
986
|
+
throw new Error("Figma asset upload response is missing image URL.");
|
|
987
|
+
}
|
|
988
|
+
return { storageKey, imageUrl };
|
|
989
|
+
}
|
|
990
|
+
function createAssetUploadUrl(endpoint, params) {
|
|
991
|
+
const base = typeof window !== "undefined" ? window.location.origin : "http://localhost";
|
|
992
|
+
const url = new URL(endpoint, base);
|
|
993
|
+
url.searchParams.set("projectId", params.projectId);
|
|
994
|
+
url.searchParams.set("imageId", params.imageId);
|
|
995
|
+
url.searchParams.set("imageFormat", params.imageFormat);
|
|
996
|
+
return url.toString();
|
|
997
|
+
}
|
|
998
|
+
function createImageRow({
|
|
999
|
+
asset,
|
|
1000
|
+
fileKey,
|
|
1001
|
+
id,
|
|
1002
|
+
imageUrl,
|
|
1003
|
+
input,
|
|
1004
|
+
nodeId,
|
|
1005
|
+
now,
|
|
1006
|
+
order,
|
|
1007
|
+
storageKey
|
|
1008
|
+
}) {
|
|
1009
|
+
const target = input.target;
|
|
1010
|
+
const targetKey = getReviewFigmaImageTargetKey(target);
|
|
1011
|
+
return {
|
|
1012
|
+
id,
|
|
1013
|
+
project_id: target.projectId,
|
|
1014
|
+
target_key: targetKey,
|
|
1015
|
+
target,
|
|
1016
|
+
target_type: target.type,
|
|
1017
|
+
page_url: target.type === "route" ? target.pageUrl : null,
|
|
1018
|
+
viewport_label: target.type === "route" ? target.viewport?.label ?? null : null,
|
|
1019
|
+
viewport_width: target.type === "route" ? target.viewport?.width ?? null : null,
|
|
1020
|
+
viewport_height: target.type === "route" ? target.viewport?.height ?? null : null,
|
|
1021
|
+
viewport_scope: target.type === "route" ? target.viewport?.scope ?? null : null,
|
|
1022
|
+
slot: target.type === "route" ? target.slot ?? null : null,
|
|
1023
|
+
figma_url: input.figmaUrl,
|
|
1024
|
+
file_key: fileKey,
|
|
1025
|
+
node_id: nodeId,
|
|
1026
|
+
image_url: imageUrl,
|
|
1027
|
+
image_format: asset.imageFormat,
|
|
1028
|
+
mime_type: asset.mimeType,
|
|
1029
|
+
storage_key: storageKey,
|
|
1030
|
+
label: normalizeOptionalText(input.label) ?? null,
|
|
1031
|
+
sort_order: order,
|
|
1032
|
+
width: asset.width ?? null,
|
|
1033
|
+
height: asset.height ?? null,
|
|
1034
|
+
byte_size: asset.byteSize ?? null,
|
|
1035
|
+
created_at: now,
|
|
1036
|
+
updated_at: now
|
|
1037
|
+
};
|
|
1038
|
+
}
|
|
1039
|
+
function createUpdatePatch(patch) {
|
|
1040
|
+
return {
|
|
1041
|
+
...patch.label !== void 0 ? { label: normalizeOptionalText(patch.label) ?? null } : {},
|
|
1042
|
+
...typeof patch.order === "number" && Number.isFinite(patch.order) ? { sort_order: patch.order } : {},
|
|
1043
|
+
updated_at: (/* @__PURE__ */ new Date()).toISOString()
|
|
1044
|
+
};
|
|
1045
|
+
}
|
|
1046
|
+
async function getNextImageOrder(listImages, target) {
|
|
1047
|
+
const images = await listImages(target);
|
|
1048
|
+
return images.length ? Math.max(...images.map((image) => image.order)) + 1 : 0;
|
|
1049
|
+
}
|
|
1050
|
+
function rowToFigmaImage(row) {
|
|
1051
|
+
return {
|
|
1052
|
+
id: row.id,
|
|
1053
|
+
projectId: row.project_id,
|
|
1054
|
+
target: row.target,
|
|
1055
|
+
figmaUrl: row.figma_url,
|
|
1056
|
+
fileKey: row.file_key,
|
|
1057
|
+
nodeId: row.node_id,
|
|
1058
|
+
imageUrl: row.image_url,
|
|
1059
|
+
imageFormat: row.image_format,
|
|
1060
|
+
mimeType: row.mime_type,
|
|
1061
|
+
label: row.label ?? void 0,
|
|
1062
|
+
order: row.sort_order,
|
|
1063
|
+
storageKey: row.storage_key,
|
|
1064
|
+
width: row.width ?? void 0,
|
|
1065
|
+
height: row.height ?? void 0,
|
|
1066
|
+
byteSize: row.byte_size ?? void 0,
|
|
1067
|
+
createdAt: row.created_at,
|
|
1068
|
+
updatedAt: row.updated_at
|
|
1069
|
+
};
|
|
1070
|
+
}
|
|
1071
|
+
function decodeAssetDataUrl(asset) {
|
|
1072
|
+
const match = asset.dataUrl.match(/^data:([^;,]+);base64,(.*)$/);
|
|
1073
|
+
if (!match) throw new Error("Valid Figma image asset data URL is required.");
|
|
1074
|
+
const mimeType = match[1]?.trim();
|
|
1075
|
+
if (!mimeType || mimeType !== asset.mimeType) {
|
|
1076
|
+
throw new Error("Figma image asset MIME type mismatch.");
|
|
1077
|
+
}
|
|
1078
|
+
const binary = globalThis.atob(match[2] ?? "");
|
|
1079
|
+
const bytes = new Uint8Array(binary.length);
|
|
1080
|
+
for (let index = 0; index < binary.length; index += 1) {
|
|
1081
|
+
bytes[index] = binary.charCodeAt(index);
|
|
1082
|
+
}
|
|
1083
|
+
return {
|
|
1084
|
+
blob: new Blob([bytes], { type: mimeType }),
|
|
1085
|
+
mimeType
|
|
1086
|
+
};
|
|
1087
|
+
}
|
|
1088
|
+
async function withTimeout2(promise, timeoutMs, message) {
|
|
1089
|
+
let timeoutId;
|
|
1090
|
+
try {
|
|
1091
|
+
return await Promise.race([
|
|
1092
|
+
promise,
|
|
1093
|
+
new Promise((_, reject) => {
|
|
1094
|
+
timeoutId = setTimeout(() => reject(new Error(message)), timeoutMs);
|
|
1095
|
+
})
|
|
1096
|
+
]);
|
|
1097
|
+
} finally {
|
|
1098
|
+
if (timeoutId) clearTimeout(timeoutId);
|
|
1099
|
+
}
|
|
1100
|
+
}
|
|
1101
|
+
function readRemoteFigmaToken(provider) {
|
|
1102
|
+
const token = typeof provider === "function" ? provider() : provider;
|
|
1103
|
+
if (typeof token === "string" && token.trim()) return token.trim();
|
|
1104
|
+
return readStoredFigmaToken();
|
|
1105
|
+
}
|
|
1106
|
+
function readStoredFigmaToken() {
|
|
1107
|
+
if (typeof window === "undefined") return "";
|
|
1108
|
+
try {
|
|
1109
|
+
return window.localStorage.getItem(FIGMA_TOKEN_STORAGE_KEY)?.trim() || "";
|
|
1110
|
+
} catch {
|
|
1111
|
+
return "";
|
|
1112
|
+
}
|
|
1113
|
+
}
|
|
1114
|
+
function createFigmaImageId() {
|
|
1115
|
+
const id = globalThis.crypto?.randomUUID?.() ?? Math.random().toString(36).slice(2);
|
|
1116
|
+
return `figma_${id.replace(/[^A-Za-z0-9_-]+/g, "-")}`;
|
|
1117
|
+
}
|
|
1118
|
+
function normalizeOptionalText(value) {
|
|
1119
|
+
if (typeof value !== "string") return void 0;
|
|
1120
|
+
return value.trim() || void 0;
|
|
1121
|
+
}
|
|
1122
|
+
function readString(value) {
|
|
1123
|
+
return typeof value === "string" && value.trim() ? value.trim() : null;
|
|
1124
|
+
}
|
|
1125
|
+
function throwRemoteStoreError(error, action) {
|
|
1126
|
+
const source = error;
|
|
1127
|
+
const message = readString(source.message) ?? `Remote Figma image ${action} failed.`;
|
|
1128
|
+
const code = readString(source.code);
|
|
1129
|
+
const details = readString(source.details);
|
|
1130
|
+
const hint = readString(source.hint);
|
|
1131
|
+
const suffix = [code, details, hint].filter(Boolean).join(" / ");
|
|
1132
|
+
throw new Error(suffix ? `${message} (${suffix})` : message);
|
|
1133
|
+
}
|
|
1134
|
+
|
|
802
1135
|
// src/figma/image.snapshot.ts
|
|
803
1136
|
function createReviewFigmaImagesSnapshot(images, options = {}) {
|
|
804
1137
|
const targetKeys = options.targets?.length ? new Set(options.targets.map(getReviewFigmaImageTargetKey)) : null;
|
|
@@ -5381,6 +5714,7 @@ function createNoopController() {
|
|
|
5381
5714
|
0 && (module.exports = {
|
|
5382
5715
|
DEFAULT_REVIEW_FIGMA_IMAGE_FORMAT,
|
|
5383
5716
|
DEFAULT_REVIEW_FIGMA_IMAGE_STORE_ENDPOINT,
|
|
5717
|
+
DEFAULT_REVIEW_FIGMA_REMOTE_IMAGES_TABLE,
|
|
5384
5718
|
DEFAULT_REVIEW_FIGMA_TOKEN_ENV_KEY,
|
|
5385
5719
|
DEFAULT_REVIEW_VIEWPORTS,
|
|
5386
5720
|
FIGMA_NODE_REF_SEPARATOR,
|
|
@@ -5388,6 +5722,8 @@ function createNoopController() {
|
|
|
5388
5722
|
REVIEW_WORKFLOW_STATUS_OPTIONS,
|
|
5389
5723
|
ReviewFigmaTokenError,
|
|
5390
5724
|
collectReviewFigmaReleaseSnapshot,
|
|
5725
|
+
createEndpointReviewFigmaImageStore,
|
|
5726
|
+
createRemoteReviewFigmaImageStore,
|
|
5391
5727
|
createReviewFigmaFrameUrl,
|
|
5392
5728
|
createReviewFigmaImageStoreClient,
|
|
5393
5729
|
createReviewFigmaImagesSnapshot,
|