@designfever/web-review-kit 0.8.2 → 0.8.4
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 +3 -3
- package/dist/{chunk-ZWJNUOYV.js → chunk-FWN7RQCW.js} +41 -58
- package/dist/chunk-FWN7RQCW.js.map +1 -0
- package/dist/index.cjs +39 -62
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +2 -6
- package/dist/index.js.map +1 -1
- package/dist/react-shell.cjs +770 -796
- package/dist/react-shell.cjs.map +1 -1
- package/dist/react-shell.js +752 -756
- package/dist/react-shell.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/release-notes-0.8.3.md +48 -0
- package/docs/release-notes-0.8.4.md +23 -0
- package/docs/testing.md +12 -4
- package/package.json +1 -1
- package/dist/chunk-ZWJNUOYV.js.map +0 -1
package/dist/index.js
CHANGED
|
@@ -10,7 +10,7 @@ import {
|
|
|
10
10
|
getReviewViewportScope,
|
|
11
11
|
localAdapter,
|
|
12
12
|
normalizeReviewItemStatus
|
|
13
|
-
} from "./chunk-
|
|
13
|
+
} from "./chunk-FWN7RQCW.js";
|
|
14
14
|
import {
|
|
15
15
|
DEFAULT_REVIEW_FIGMA_IMAGE_STORE_ENDPOINT,
|
|
16
16
|
DEFAULT_REVIEW_FIGMA_TOKEN_ENV_KEY,
|
|
@@ -113,7 +113,7 @@ function normalizeItemForSupabaseCreate(item, source, options) {
|
|
|
113
113
|
const normalizedStatus = normalizeReviewItemStatus(item.status);
|
|
114
114
|
const routeKey = item.routeKey || item.normalizedPath || "/";
|
|
115
115
|
const viewport = item.viewport ?? { width: 390, height: 720 };
|
|
116
|
-
|
|
116
|
+
return {
|
|
117
117
|
...item,
|
|
118
118
|
id,
|
|
119
119
|
reviewNumber: void 0,
|
|
@@ -134,10 +134,6 @@ function normalizeItemForSupabaseCreate(item, source, options) {
|
|
|
134
134
|
createdAt: now,
|
|
135
135
|
updatedAt: now
|
|
136
136
|
};
|
|
137
|
-
return {
|
|
138
|
-
...nextItem,
|
|
139
|
-
externalIssueUrl: nextItem.externalIssueUrl ?? buildSupabaseReviewUrl(nextItem, source, options)
|
|
140
|
-
};
|
|
141
137
|
}
|
|
142
138
|
async function createItemWithRpc(item, source, options) {
|
|
143
139
|
const rpcName = options.createRpc ?? DEFAULT_SUPABASE_CREATE_REVIEW_ITEM_RPC;
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/adapters/supabase.ts","../src/figma/remote.image.store.ts"],"sourcesContent":["import { normalizeReviewItemStatus } from '../status';\nimport type {\n ReviewItem,\n ReviewItemKind,\n ReviewItemStatus,\n ReviewSource,\n SupabaseReviewAdapterOptions,\n WebReviewKitAdapter,\n} from '../types';\n\nconst DEFAULT_SUPABASE_REVIEW_TABLE = 'review_items';\nconst DEFAULT_SUPABASE_REVIEW_SOURCE = 'supabase';\nconst DEFAULT_SUPABASE_CREATE_REVIEW_ITEM_RPC = 'create_review_item';\n\ntype SupabaseReviewRow = {\n id: string;\n project_id: string;\n route_key: string;\n source: string;\n review_number?: number | null;\n status: string;\n item: unknown;\n created_at: string;\n updated_at: string;\n};\n\ntype SupabaseResponse<T> = {\n data: T | null;\n error: { message?: string; code?: string } | null;\n};\n\nexport function supabaseAdapter(\n options: SupabaseReviewAdapterOptions\n): WebReviewKitAdapter {\n const tableName = options.table ?? DEFAULT_SUPABASE_REVIEW_TABLE;\n const source = options.source ?? DEFAULT_SUPABASE_REVIEW_SOURCE;\n\n const fromTable = () => options.client.from(tableName);\n\n return {\n async get(id) {\n const row = await unwrapResponse<SupabaseReviewRow | null>(\n fromTable().select('*').eq('id', id).maybeSingle(),\n 'supabase get review item'\n );\n\n return row ? rowToReviewItem(row, options) : null;\n },\n\n async list(query) {\n let request = fromTable()\n .select('*')\n .eq('project_id', query.projectId)\n .eq('source', query.source ?? source);\n const routeKey = query.routeKey ?? query.normalizedPath;\n\n if (routeKey) {\n request = request.eq('route_key', routeKey);\n }\n if (query.status) {\n request = request.eq(\n 'status',\n normalizeReviewItemStatus(query.status)\n );\n }\n\n const rows = await unwrapResponse<SupabaseReviewRow[]>(\n request.order('created_at', { ascending: false }),\n 'supabase list review items'\n );\n\n return (rows ?? []).flatMap((row) => {\n const item = rowToReviewItem(row, options);\n return item ? [item] : [];\n });\n },\n\n async create(item) {\n const nextItem = normalizeItemForSupabaseCreate(item, source, options);\n\n if (options.unsafeClientReviewNumberFallback) {\n throw new Error(\n 'supabase create review item: unsafeClientReviewNumberFallback is no longer supported. Use create_review_item RPC with database-backed review_number sequence.'\n );\n }\n\n return createItemWithRpc(nextItem, source, options);\n },\n\n async update(id, patch) {\n const current = await this.get(id);\n if (!current) throw new Error(`Review item not found: ${id}`);\n\n const nextStatus = patch.status\n ? normalizeReviewItemStatus(patch.status)\n : current.status;\n const nextItem: ReviewItem = {\n ...current,\n ...patch,\n id,\n status: nextStatus,\n createdAt: current.createdAt,\n updatedAt: new Date().toISOString(),\n };\n const patchRow = itemToRowPatch(nextItem, source, options);\n const updated = await unwrapResponse<SupabaseReviewRow>(\n fromTable().update(patchRow).eq('id', id).select('*').single(),\n 'supabase update review item'\n );\n\n return rowToReviewItem(updated, options) ?? nextItem;\n },\n\n async remove(id) {\n await unwrapResponse<null>(\n fromTable().delete().eq('id', id),\n 'supabase delete review item'\n );\n },\n };\n}\n\nfunction normalizeItemForSupabaseCreate(\n item: ReviewItem,\n source: ReviewSource,\n options: SupabaseReviewAdapterOptions\n): ReviewItem {\n const now = new Date().toISOString();\n const id = createSupabaseReviewItemId();\n const normalizedStatus = normalizeReviewItemStatus(item.status);\n const routeKey = item.routeKey || item.normalizedPath || '/';\n const viewport = item.viewport ?? { width: 390, height: 720 };\n const nextItem: ReviewItem = {\n ...item,\n id,\n reviewNumber: undefined,\n projectId: options.projectId,\n routeKey,\n normalizedPath: item.normalizedPath || routeKey,\n viewport,\n status: normalizedStatus,\n externalIssueId: id,\n externalIssueUrl: buildSupabaseReviewUrl(\n { routeKey, normalizedPath: item.normalizedPath || routeKey, viewport },\n source,\n options,\n id\n ),\n submittedAt: item.submittedAt ?? now,\n submitStatus: item.submitStatus ?? 'submitted',\n createdAt: now,\n updatedAt: now,\n };\n\n return {\n ...nextItem,\n externalIssueUrl:\n nextItem.externalIssueUrl ??\n buildSupabaseReviewUrl(nextItem, source, options),\n };\n}\n\nasync function createItemWithRpc(\n item: ReviewItem,\n source: ReviewSource,\n options: SupabaseReviewAdapterOptions\n) {\n const rpcName =\n options.createRpc ?? DEFAULT_SUPABASE_CREATE_REVIEW_ITEM_RPC;\n\n if (!options.client.rpc) {\n throw new Error(\n `supabase create review item: ${rpcName} rpc is required`\n );\n }\n\n const row = await unwrapResponse<SupabaseReviewRow>(\n options.client.rpc(rpcName, {\n p_id: item.id,\n p_project_id: options.projectId,\n p_route_key: item.routeKey || item.normalizedPath || '/',\n p_source: source,\n p_status: normalizeReviewItemStatus(item.status),\n p_item: item,\n }),\n `supabase create review item rpc ${rpcName}`\n );\n\n return rowToReviewItem(row, options) ?? item;\n}\n\nfunction itemToRow(\n item: ReviewItem,\n source: ReviewSource,\n options: SupabaseReviewAdapterOptions\n): SupabaseReviewRow {\n const now = new Date().toISOString();\n const updatedAt = item.updatedAt || now;\n\n return {\n id: item.id,\n project_id: options.projectId,\n route_key: item.routeKey || item.normalizedPath || '/',\n source,\n review_number: item.reviewNumber ?? null,\n status: normalizeReviewItemStatus(item.status),\n item: {\n ...item,\n projectId: options.projectId,\n status: normalizeReviewItemStatus(item.status),\n updatedAt,\n },\n created_at: item.createdAt || now,\n updated_at: updatedAt,\n };\n}\n\nfunction itemToRowPatch(\n item: ReviewItem,\n source: ReviewSource,\n options: SupabaseReviewAdapterOptions\n) {\n const row = itemToRow(item, source, options);\n return {\n route_key: row.route_key,\n review_number: row.review_number,\n status: row.status,\n item: row.item,\n updated_at: row.updated_at,\n };\n}\n\nfunction rowToReviewItem(\n row: SupabaseReviewRow,\n options: SupabaseReviewAdapterOptions\n): ReviewItem | null {\n if (!row.item || typeof row.item !== 'object') return null;\n\n const item = row.item as Partial<Omit<ReviewItem, 'kind'>> & {\n kind?: string;\n };\n const status = normalizeReviewItemStatus(\n (row.status || item.status || 'todo') as ReviewItemStatus\n );\n const routeKey = row.route_key || item.routeKey || item.normalizedPath || '/';\n const viewport = item.viewport ?? { width: 390, height: 720 };\n const now = new Date().toISOString();\n const kind = normalizeReviewItemKind(item);\n if (!kind) return null;\n\n return {\n ...(item as ReviewItem),\n id: row.id,\n reviewNumber: row.review_number ?? item.reviewNumber,\n projectId: row.project_id || item.projectId || options.projectId,\n routeKey,\n pageUrl: item.pageUrl || toAbsoluteReviewUrl(routeKey),\n normalizedPath: item.normalizedPath || routeKey,\n kind,\n comment: item.comment || '',\n status,\n viewport,\n externalIssueId: item.externalIssueId ?? row.id,\n externalIssueUrl:\n item.externalIssueUrl ??\n buildSupabaseReviewUrl(\n { routeKey, normalizedPath: routeKey, viewport },\n row.source,\n options,\n row.id\n ),\n submittedAt: item.submittedAt ?? row.created_at,\n submitStatus: item.submitStatus ?? 'submitted',\n createdAt: item.createdAt ?? row.created_at ?? now,\n updatedAt: row.updated_at ?? item.updatedAt ?? now,\n };\n}\n\nfunction normalizeReviewItemKind(\n item: Partial<Omit<ReviewItem, 'kind'>> & { kind?: string }\n): ReviewItemKind | null {\n if (item.kind === 'area' || item.kind === 'dom') return item.kind;\n return null;\n}\n\nasync function unwrapResponse<T>(\n request: Promise<SupabaseResponse<T>> | SupabaseResponse<T>,\n label: string\n) {\n const { data, error } = await request;\n if (error) {\n throw new Error(`${label}: ${error.message ?? error.code ?? 'failed'}`);\n }\n return data as T;\n}\n\nfunction buildSupabaseReviewUrl(\n item: Pick<ReviewItem, 'routeKey' | 'normalizedPath' | 'viewport'>,\n source: ReviewSource,\n options: SupabaseReviewAdapterOptions,\n itemId?: string\n) {\n if (typeof window === 'undefined') return undefined;\n\n const url = new URL(\n normalizeReviewUrlPath(options.reviewPathPrefix),\n window.location.origin\n );\n url.searchParams.set('source', source);\n url.searchParams.set('target', item.routeKey || item.normalizedPath || '/');\n url.searchParams.set('w', String(Math.round(item.viewport.width)));\n url.searchParams.set('h', String(Math.round(item.viewport.height)));\n if (itemId) url.searchParams.set('item', itemId);\n return url.toString();\n}\n\nfunction normalizeReviewUrlPath(value = '/review') {\n const raw = value.trim() || '/review';\n const path = raw.startsWith('/') ? raw : `/${raw}`;\n return path.endsWith('/') ? path : `${path}/`;\n}\n\nfunction toAbsoluteReviewUrl(path: string) {\n if (typeof window === 'undefined') return path;\n return new URL(path, window.location.origin).toString();\n}\n\nfunction createSupabaseReviewItemId() {\n if (typeof crypto !== 'undefined' && crypto.randomUUID) {\n return crypto.randomUUID();\n }\n\n return `review-${Date.now()}-${Math.random().toString(36).slice(2, 10)}`;\n}\n","import type {\n AddReviewFigmaImageInput,\n ReviewFigmaImage,\n ReviewFigmaImageAssetInput,\n ReviewFigmaImageFormat,\n ReviewFigmaImageStore,\n ReviewFigmaImageTarget,\n UpdateReviewFigmaImageInput,\n} from './image.types';\nimport {\n createReviewFigmaClientRenderedAsset,\n getReviewFigmaImageTargetKey,\n type ReviewFigmaImageClientRenderOptions,\n} from './image.store';\nimport { parseReviewFigmaNodeRef } from './parse';\n\nexport const DEFAULT_REVIEW_FIGMA_REMOTE_IMAGES_TABLE = 'review_figma_images';\nconst DEFAULT_REVIEW_FIGMA_REMOTE_UPLOAD_TIMEOUT_MS = 30000;\nconst FIGMA_TOKEN_STORAGE_KEY = 'figma-token';\n\ntype ReviewFigmaRemoteTokenProvider =\n | string\n | null\n | undefined\n | (() => string | null | undefined);\n\nexport type ReviewFigmaRemoteImageRow = {\n id: string;\n project_id: string;\n target_key: string;\n target: ReviewFigmaImageTarget;\n target_type: 'route' | 'figma-node';\n page_url: string | null;\n viewport_label: string | null;\n viewport_width: number | null;\n viewport_height: number | null;\n viewport_scope: 'mobile' | 'tablet' | 'desktop' | 'wide' | null;\n slot: string | null;\n figma_url: string;\n file_key: string;\n node_id: string;\n image_url: string;\n image_format: ReviewFigmaImageFormat;\n mime_type: string;\n storage_key: string;\n label: string | null;\n sort_order: number;\n width: number | null;\n height: number | null;\n byte_size: number | null;\n created_at: string;\n updated_at: string;\n};\n\ntype ReviewFigmaRemoteDbResult<T = unknown> = {\n data: T | null;\n error: unknown;\n};\n\ntype ReviewFigmaRemoteDbQuery<T = unknown> = PromiseLike<\n ReviewFigmaRemoteDbResult<T>\n> & {\n delete(): ReviewFigmaRemoteDbQuery<T>;\n eq(column: string, value: unknown): ReviewFigmaRemoteDbQuery<T>;\n insert(value: unknown): ReviewFigmaRemoteDbQuery<T>;\n order(\n column: string,\n options?: { ascending?: boolean }\n ): ReviewFigmaRemoteDbQuery<T>;\n select(columns?: string): ReviewFigmaRemoteDbQuery<T>;\n single(): PromiseLike<ReviewFigmaRemoteDbResult<T>>;\n update(value: unknown): ReviewFigmaRemoteDbQuery<T>;\n};\n\nexport type ReviewFigmaRemoteDbClient = {\n from(table: string): ReviewFigmaRemoteDbQuery;\n};\n\nexport type ReviewFigmaRemoteAssetUploadResponse = {\n storageKey?: unknown;\n r2Key?: unknown;\n imageUrl?: unknown;\n publicUrl?: unknown;\n error?: unknown;\n};\n\nexport type RemoteReviewFigmaImageStoreOptions = {\n client: ReviewFigmaRemoteDbClient;\n uploadEndpoint: string;\n table?: string;\n fetch?: typeof fetch;\n token?: ReviewFigmaRemoteTokenProvider;\n clientRender?: boolean | ReviewFigmaImageClientRenderOptions;\n uploadTimeoutMs?: number;\n};\n\nexport function createRemoteReviewFigmaImageStore({\n client,\n uploadEndpoint,\n table = DEFAULT_REVIEW_FIGMA_REMOTE_IMAGES_TABLE,\n fetch: fetchOption,\n token,\n clientRender = true,\n uploadTimeoutMs = DEFAULT_REVIEW_FIGMA_REMOTE_UPLOAD_TIMEOUT_MS,\n}: RemoteReviewFigmaImageStoreOptions): ReviewFigmaImageStore {\n const listImages = async (target: ReviewFigmaImageTarget) => {\n const targetKey = getReviewFigmaImageTargetKey(target);\n const { data, error } = await client\n .from(table)\n .select('*')\n .eq('project_id', target.projectId)\n .eq('target_key', targetKey)\n .order('sort_order', { ascending: true })\n .order('created_at', { ascending: true });\n\n if (error) throwRemoteStoreError(error, 'list Figma images');\n return ((data ?? []) as ReviewFigmaRemoteImageRow[]).map(rowToFigmaImage);\n };\n\n return {\n listImages,\n async addImage(input) {\n const ref = parseReviewFigmaNodeRef(input.figmaUrl);\n if (!ref) {\n throw new Error('A Figma node link or fileKey->nodeId value is required.');\n }\n\n const id = createFigmaImageId();\n const asset = await resolveFigmaImageAsset({\n input,\n token,\n clientRender,\n fetch: fetchOption,\n });\n const uploaded = await uploadFigmaAsset({\n asset,\n endpoint: uploadEndpoint,\n fetch: fetchOption,\n imageId: id,\n projectId: input.target.projectId,\n timeoutMs: uploadTimeoutMs,\n });\n const order =\n typeof input.order === 'number' && Number.isFinite(input.order)\n ? input.order\n : await getNextImageOrder(listImages, input.target);\n const now = new Date().toISOString();\n const row = createImageRow({\n asset,\n fileKey: ref.fileKey,\n id,\n imageUrl: uploaded.imageUrl,\n input,\n nodeId: ref.nodeId,\n now,\n order,\n storageKey: uploaded.storageKey,\n });\n const { data, error } = await client\n .from(table)\n .insert(row)\n .select('*')\n .single();\n\n if (error) throwRemoteStoreError(error, 'insert Figma image');\n return rowToFigmaImage(data as ReviewFigmaRemoteImageRow);\n },\n async updateImage(id, patch) {\n const { data, error } = await client\n .from(table)\n .update(createUpdatePatch(patch))\n .eq('id', id)\n .select('*')\n .single();\n\n if (error) throwRemoteStoreError(error, 'update Figma image');\n return rowToFigmaImage(data as ReviewFigmaRemoteImageRow);\n },\n async reorderImages(input) {\n const current = await listImages(input.target);\n const currentIds = new Set(current.map((image) => image.id));\n const nextIds = [\n ...input.imageIds.filter((id) => currentIds.has(id)),\n ...current\n .map((image) => image.id)\n .filter((id) => !input.imageIds.includes(id)),\n ];\n const targetKey = getReviewFigmaImageTargetKey(input.target);\n const updatedAt = new Date().toISOString();\n\n const results = await Promise.all(\n nextIds.map((id, index) =>\n client\n .from(table)\n .update({ sort_order: index, updated_at: updatedAt })\n .eq('id', id)\n .eq('project_id', input.target.projectId)\n .eq('target_key', targetKey)\n )\n );\n const failed = results.find((result) => result.error);\n if (failed?.error) throwRemoteStoreError(failed.error, 'reorder Figma images');\n\n return listImages(input.target);\n },\n async deleteImage(id) {\n const { error } = await client.from(table).delete().eq('id', id);\n if (error) throwRemoteStoreError(error, 'delete Figma image');\n },\n };\n}\n\nasync function resolveFigmaImageAsset({\n input,\n token,\n clientRender,\n fetch,\n}: {\n input: AddReviewFigmaImageInput;\n token: ReviewFigmaRemoteTokenProvider;\n clientRender: boolean | ReviewFigmaImageClientRenderOptions;\n fetch: typeof globalThis.fetch | undefined;\n}) {\n if (input.asset) return input.asset;\n if (!clientRender) {\n throw new Error('Figma image asset is required when clientRender is disabled.');\n }\n\n const renderOptions = clientRender === true ? {} : clientRender;\n const figmaToken = readRemoteFigmaToken(renderOptions.token ?? token);\n if (!figmaToken) throw new Error('Figma token is required.');\n\n return createReviewFigmaClientRenderedAsset({\n ...renderOptions,\n fetch,\n figmaUrl: input.figmaUrl,\n token: figmaToken,\n });\n}\n\nasync function uploadFigmaAsset({\n asset,\n endpoint,\n fetch: fetchOption,\n imageId,\n projectId,\n timeoutMs,\n}: {\n asset: ReviewFigmaImageAssetInput;\n endpoint: string;\n fetch: typeof globalThis.fetch | undefined;\n imageId: string;\n projectId: string;\n timeoutMs: number;\n}) {\n const requestFetch = fetchOption ?? globalThis.fetch;\n if (!requestFetch) throw new Error('Figma asset upload requires fetch.');\n\n const { blob, mimeType } = decodeAssetDataUrl(asset);\n const response = await withTimeout(\n requestFetch(\n createAssetUploadUrl(endpoint, {\n imageFormat: asset.imageFormat,\n imageId,\n projectId,\n }),\n {\n method: 'POST',\n headers: {\n 'Content-Type': mimeType,\n },\n body: blob,\n }\n ),\n timeoutMs,\n 'Figma asset upload timed out.'\n );\n const data = (await response.json().catch(() => null)) as\n | ReviewFigmaRemoteAssetUploadResponse\n | null;\n\n if (!response.ok) {\n throw new Error(\n readString(data?.error) || `Figma asset upload failed with ${response.status}`\n );\n }\n\n const storageKey = readString(data?.storageKey) ?? readString(data?.r2Key);\n const imageUrl = readString(data?.imageUrl) ?? readString(data?.publicUrl);\n if (!storageKey || !imageUrl) {\n throw new Error('Figma asset upload response is missing image URL.');\n }\n\n return { storageKey, imageUrl };\n}\n\nfunction createAssetUploadUrl(\n endpoint: string,\n params: {\n imageFormat: ReviewFigmaImageFormat;\n imageId: string;\n projectId: string;\n }\n) {\n const base =\n typeof window !== 'undefined' ? window.location.origin : 'http://localhost';\n const url = new URL(endpoint, base);\n url.searchParams.set('projectId', params.projectId);\n url.searchParams.set('imageId', params.imageId);\n url.searchParams.set('imageFormat', params.imageFormat);\n return url.toString();\n}\n\nfunction createImageRow({\n asset,\n fileKey,\n id,\n imageUrl,\n input,\n nodeId,\n now,\n order,\n storageKey,\n}: {\n asset: ReviewFigmaImageAssetInput;\n fileKey: string;\n id: string;\n imageUrl: string;\n input: AddReviewFigmaImageInput;\n nodeId: string;\n now: string;\n order: number;\n storageKey: string;\n}): ReviewFigmaRemoteImageRow {\n const target = input.target;\n const targetKey = getReviewFigmaImageTargetKey(target);\n\n return {\n id,\n project_id: target.projectId,\n target_key: targetKey,\n target,\n target_type: target.type,\n page_url: target.type === 'route' ? target.pageUrl : null,\n viewport_label: target.type === 'route' ? target.viewport?.label ?? null : null,\n viewport_width: target.type === 'route' ? target.viewport?.width ?? null : null,\n viewport_height: target.type === 'route' ? target.viewport?.height ?? null : null,\n viewport_scope: target.type === 'route' ? target.viewport?.scope ?? null : null,\n slot: target.type === 'route' ? target.slot ?? null : null,\n figma_url: input.figmaUrl,\n file_key: fileKey,\n node_id: nodeId,\n image_url: imageUrl,\n image_format: asset.imageFormat,\n mime_type: asset.mimeType,\n storage_key: storageKey,\n label: normalizeOptionalText(input.label) ?? null,\n sort_order: order,\n width: asset.width ?? null,\n height: asset.height ?? null,\n byte_size: asset.byteSize ?? null,\n created_at: now,\n updated_at: now,\n };\n}\n\nfunction createUpdatePatch(patch: UpdateReviewFigmaImageInput) {\n return {\n ...(patch.label !== undefined\n ? { label: normalizeOptionalText(patch.label) ?? null }\n : {}),\n ...(typeof patch.order === 'number' && Number.isFinite(patch.order)\n ? { sort_order: patch.order }\n : {}),\n updated_at: new Date().toISOString(),\n };\n}\n\nasync function getNextImageOrder(\n listImages: (target: ReviewFigmaImageTarget) => Promise<ReviewFigmaImage[]>,\n target: ReviewFigmaImageTarget\n) {\n const images = await listImages(target);\n return images.length\n ? Math.max(...images.map((image) => image.order)) + 1\n : 0;\n}\n\nfunction rowToFigmaImage(row: ReviewFigmaRemoteImageRow): ReviewFigmaImage {\n return {\n id: row.id,\n projectId: row.project_id,\n target: row.target,\n figmaUrl: row.figma_url,\n fileKey: row.file_key,\n nodeId: row.node_id,\n imageUrl: row.image_url,\n imageFormat: row.image_format,\n mimeType: row.mime_type,\n label: row.label ?? undefined,\n order: row.sort_order,\n storageKey: row.storage_key,\n width: row.width ?? undefined,\n height: row.height ?? undefined,\n byteSize: row.byte_size ?? undefined,\n createdAt: row.created_at,\n updatedAt: row.updated_at,\n };\n}\n\nfunction decodeAssetDataUrl(asset: ReviewFigmaImageAssetInput) {\n const match = asset.dataUrl.match(/^data:([^;,]+);base64,(.*)$/);\n if (!match) throw new Error('Valid Figma image asset data URL is required.');\n\n const mimeType = match[1]?.trim();\n if (!mimeType || mimeType !== asset.mimeType) {\n throw new Error('Figma image asset MIME type mismatch.');\n }\n\n const binary = globalThis.atob(match[2] ?? '');\n const bytes = new Uint8Array(binary.length);\n for (let index = 0; index < binary.length; index += 1) {\n bytes[index] = binary.charCodeAt(index);\n }\n\n return {\n blob: new Blob([bytes], { type: mimeType }),\n mimeType,\n };\n}\n\nasync function withTimeout<T>(\n promise: Promise<T>,\n timeoutMs: number,\n message: string\n) {\n let timeoutId: ReturnType<typeof setTimeout> | undefined;\n try {\n return await Promise.race([\n promise,\n new Promise<T>((_, reject) => {\n timeoutId = setTimeout(() => reject(new Error(message)), timeoutMs);\n }),\n ]);\n } finally {\n if (timeoutId) clearTimeout(timeoutId);\n }\n}\n\nfunction readRemoteFigmaToken(provider: ReviewFigmaRemoteTokenProvider) {\n const token = typeof provider === 'function' ? provider() : provider;\n if (typeof token === 'string' && token.trim()) return token.trim();\n return readStoredFigmaToken();\n}\n\nfunction readStoredFigmaToken() {\n if (typeof window === 'undefined') return '';\n\n try {\n return window.localStorage.getItem(FIGMA_TOKEN_STORAGE_KEY)?.trim() || '';\n } catch {\n return '';\n }\n}\n\nfunction createFigmaImageId() {\n const id =\n globalThis.crypto?.randomUUID?.() ?? Math.random().toString(36).slice(2);\n return `figma_${id.replace(/[^A-Za-z0-9_-]+/g, '-')}`;\n}\n\nfunction normalizeOptionalText(value: string | null | undefined) {\n if (typeof value !== 'string') return undefined;\n return value.trim() || undefined;\n}\n\nfunction readString(value: unknown) {\n return typeof value === 'string' && value.trim() ? value.trim() : null;\n}\n\nfunction throwRemoteStoreError(error: unknown, action: string): never {\n const source = error as {\n code?: unknown;\n details?: unknown;\n hint?: unknown;\n message?: unknown;\n };\n const message = readString(source.message) ?? `Remote Figma image ${action} failed.`;\n const code = readString(source.code);\n const details = readString(source.details);\n const hint = readString(source.hint);\n const suffix = [code, details, hint].filter(Boolean).join(' / ');\n\n throw new Error(suffix ? `${message} (${suffix})` : message);\n}\n"],"mappings":"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","names":[]}
|
|
1
|
+
{"version":3,"sources":["../src/adapters/supabase.ts","../src/figma/remote.image.store.ts"],"sourcesContent":["import { normalizeReviewItemStatus } from '../status';\nimport type {\n ReviewItem,\n ReviewItemKind,\n ReviewItemStatus,\n ReviewSource,\n SupabaseReviewAdapterOptions,\n WebReviewKitAdapter,\n} from '../types';\n\nconst DEFAULT_SUPABASE_REVIEW_TABLE = 'review_items';\nconst DEFAULT_SUPABASE_REVIEW_SOURCE = 'supabase';\nconst DEFAULT_SUPABASE_CREATE_REVIEW_ITEM_RPC = 'create_review_item';\n\ntype SupabaseReviewRow = {\n id: string;\n project_id: string;\n route_key: string;\n source: string;\n review_number?: number | null;\n status: string;\n item: unknown;\n created_at: string;\n updated_at: string;\n};\n\ntype SupabaseResponse<T> = {\n data: T | null;\n error: { message?: string; code?: string } | null;\n};\n\nexport function supabaseAdapter(\n options: SupabaseReviewAdapterOptions\n): WebReviewKitAdapter {\n const tableName = options.table ?? DEFAULT_SUPABASE_REVIEW_TABLE;\n const source = options.source ?? DEFAULT_SUPABASE_REVIEW_SOURCE;\n\n const fromTable = () => options.client.from(tableName);\n\n return {\n async get(id) {\n const row = await unwrapResponse<SupabaseReviewRow | null>(\n fromTable().select('*').eq('id', id).maybeSingle(),\n 'supabase get review item'\n );\n\n return row ? rowToReviewItem(row, options) : null;\n },\n\n async list(query) {\n let request = fromTable()\n .select('*')\n .eq('project_id', query.projectId)\n .eq('source', query.source ?? source);\n const routeKey = query.routeKey ?? query.normalizedPath;\n\n if (routeKey) {\n request = request.eq('route_key', routeKey);\n }\n if (query.status) {\n request = request.eq(\n 'status',\n normalizeReviewItemStatus(query.status)\n );\n }\n\n const rows = await unwrapResponse<SupabaseReviewRow[]>(\n request.order('created_at', { ascending: false }),\n 'supabase list review items'\n );\n\n return (rows ?? []).flatMap((row) => {\n const item = rowToReviewItem(row, options);\n return item ? [item] : [];\n });\n },\n\n async create(item) {\n const nextItem = normalizeItemForSupabaseCreate(item, source, options);\n\n if (options.unsafeClientReviewNumberFallback) {\n throw new Error(\n 'supabase create review item: unsafeClientReviewNumberFallback is no longer supported. Use create_review_item RPC with database-backed review_number sequence.'\n );\n }\n\n return createItemWithRpc(nextItem, source, options);\n },\n\n async update(id, patch) {\n const current = await this.get(id);\n if (!current) throw new Error(`Review item not found: ${id}`);\n\n const nextStatus = patch.status\n ? normalizeReviewItemStatus(patch.status)\n : current.status;\n const nextItem: ReviewItem = {\n ...current,\n ...patch,\n id,\n status: nextStatus,\n createdAt: current.createdAt,\n updatedAt: new Date().toISOString(),\n };\n const patchRow = itemToRowPatch(nextItem, source, options);\n const updated = await unwrapResponse<SupabaseReviewRow>(\n fromTable().update(patchRow).eq('id', id).select('*').single(),\n 'supabase update review item'\n );\n\n return rowToReviewItem(updated, options) ?? nextItem;\n },\n\n async remove(id) {\n await unwrapResponse<null>(\n fromTable().delete().eq('id', id),\n 'supabase delete review item'\n );\n },\n };\n}\n\nfunction normalizeItemForSupabaseCreate(\n item: ReviewItem,\n source: ReviewSource,\n options: SupabaseReviewAdapterOptions\n): ReviewItem {\n const now = new Date().toISOString();\n const id = createSupabaseReviewItemId();\n const normalizedStatus = normalizeReviewItemStatus(item.status);\n const routeKey = item.routeKey || item.normalizedPath || '/';\n const viewport = item.viewport ?? { width: 390, height: 720 };\n return {\n ...item,\n id,\n reviewNumber: undefined,\n projectId: options.projectId,\n routeKey,\n normalizedPath: item.normalizedPath || routeKey,\n viewport,\n status: normalizedStatus,\n externalIssueId: id,\n externalIssueUrl: buildSupabaseReviewUrl(\n { routeKey, normalizedPath: item.normalizedPath || routeKey, viewport },\n source,\n options,\n id\n ),\n submittedAt: item.submittedAt ?? now,\n submitStatus: item.submitStatus ?? 'submitted',\n createdAt: now,\n updatedAt: now,\n };\n}\n\nasync function createItemWithRpc(\n item: ReviewItem,\n source: ReviewSource,\n options: SupabaseReviewAdapterOptions\n) {\n const rpcName =\n options.createRpc ?? DEFAULT_SUPABASE_CREATE_REVIEW_ITEM_RPC;\n\n if (!options.client.rpc) {\n throw new Error(\n `supabase create review item: ${rpcName} rpc is required`\n );\n }\n\n const row = await unwrapResponse<SupabaseReviewRow>(\n options.client.rpc(rpcName, {\n p_id: item.id,\n p_project_id: options.projectId,\n p_route_key: item.routeKey || item.normalizedPath || '/',\n p_source: source,\n p_status: normalizeReviewItemStatus(item.status),\n p_item: item,\n }),\n `supabase create review item rpc ${rpcName}`\n );\n\n return rowToReviewItem(row, options) ?? item;\n}\n\nfunction itemToRow(\n item: ReviewItem,\n source: ReviewSource,\n options: SupabaseReviewAdapterOptions\n): SupabaseReviewRow {\n const now = new Date().toISOString();\n const updatedAt = item.updatedAt || now;\n\n return {\n id: item.id,\n project_id: options.projectId,\n route_key: item.routeKey || item.normalizedPath || '/',\n source,\n review_number: item.reviewNumber ?? null,\n status: normalizeReviewItemStatus(item.status),\n item: {\n ...item,\n projectId: options.projectId,\n status: normalizeReviewItemStatus(item.status),\n updatedAt,\n },\n created_at: item.createdAt || now,\n updated_at: updatedAt,\n };\n}\n\nfunction itemToRowPatch(\n item: ReviewItem,\n source: ReviewSource,\n options: SupabaseReviewAdapterOptions\n) {\n const row = itemToRow(item, source, options);\n return {\n route_key: row.route_key,\n review_number: row.review_number,\n status: row.status,\n item: row.item,\n updated_at: row.updated_at,\n };\n}\n\nfunction rowToReviewItem(\n row: SupabaseReviewRow,\n options: SupabaseReviewAdapterOptions\n): ReviewItem | null {\n if (!row.item || typeof row.item !== 'object') return null;\n\n const item = row.item as Partial<Omit<ReviewItem, 'kind'>> & {\n kind?: string;\n };\n const status = normalizeReviewItemStatus(\n (row.status || item.status || 'todo') as ReviewItemStatus\n );\n const routeKey = row.route_key || item.routeKey || item.normalizedPath || '/';\n const viewport = item.viewport ?? { width: 390, height: 720 };\n const now = new Date().toISOString();\n const kind = normalizeReviewItemKind(item);\n if (!kind) return null;\n\n return {\n ...(item as ReviewItem),\n id: row.id,\n reviewNumber: row.review_number ?? item.reviewNumber,\n projectId: row.project_id || item.projectId || options.projectId,\n routeKey,\n pageUrl: item.pageUrl || toAbsoluteReviewUrl(routeKey),\n normalizedPath: item.normalizedPath || routeKey,\n kind,\n comment: item.comment || '',\n status,\n viewport,\n externalIssueId: item.externalIssueId ?? row.id,\n externalIssueUrl:\n item.externalIssueUrl ??\n buildSupabaseReviewUrl(\n { routeKey, normalizedPath: routeKey, viewport },\n row.source,\n options,\n row.id\n ),\n submittedAt: item.submittedAt ?? row.created_at,\n submitStatus: item.submitStatus ?? 'submitted',\n createdAt: item.createdAt ?? row.created_at ?? now,\n updatedAt: row.updated_at ?? item.updatedAt ?? now,\n };\n}\n\nfunction normalizeReviewItemKind(\n item: Partial<Omit<ReviewItem, 'kind'>> & { kind?: string }\n): ReviewItemKind | null {\n if (item.kind === 'area' || item.kind === 'dom') return item.kind;\n return null;\n}\n\nasync function unwrapResponse<T>(\n request: Promise<SupabaseResponse<T>> | SupabaseResponse<T>,\n label: string\n) {\n const { data, error } = await request;\n if (error) {\n throw new Error(`${label}: ${error.message ?? error.code ?? 'failed'}`);\n }\n return data as T;\n}\n\nfunction buildSupabaseReviewUrl(\n item: Pick<ReviewItem, 'routeKey' | 'normalizedPath' | 'viewport'>,\n source: ReviewSource,\n options: SupabaseReviewAdapterOptions,\n itemId?: string\n) {\n if (typeof window === 'undefined') return undefined;\n\n const url = new URL(\n normalizeReviewUrlPath(options.reviewPathPrefix),\n window.location.origin\n );\n url.searchParams.set('source', source);\n url.searchParams.set('target', item.routeKey || item.normalizedPath || '/');\n url.searchParams.set('w', String(Math.round(item.viewport.width)));\n url.searchParams.set('h', String(Math.round(item.viewport.height)));\n if (itemId) url.searchParams.set('item', itemId);\n return url.toString();\n}\n\nfunction normalizeReviewUrlPath(value = '/review') {\n const raw = value.trim() || '/review';\n const path = raw.startsWith('/') ? raw : `/${raw}`;\n return path.endsWith('/') ? path : `${path}/`;\n}\n\nfunction toAbsoluteReviewUrl(path: string) {\n if (typeof window === 'undefined') return path;\n return new URL(path, window.location.origin).toString();\n}\n\nfunction createSupabaseReviewItemId() {\n if (typeof crypto !== 'undefined' && crypto.randomUUID) {\n return crypto.randomUUID();\n }\n\n return `review-${Date.now()}-${Math.random().toString(36).slice(2, 10)}`;\n}\n","import type {\n AddReviewFigmaImageInput,\n ReviewFigmaImage,\n ReviewFigmaImageAssetInput,\n ReviewFigmaImageFormat,\n ReviewFigmaImageStore,\n ReviewFigmaImageTarget,\n UpdateReviewFigmaImageInput,\n} from './image.types';\nimport {\n createReviewFigmaClientRenderedAsset,\n getReviewFigmaImageTargetKey,\n type ReviewFigmaImageClientRenderOptions,\n} from './image.store';\nimport { parseReviewFigmaNodeRef } from './parse';\n\nexport const DEFAULT_REVIEW_FIGMA_REMOTE_IMAGES_TABLE = 'review_figma_images';\nconst DEFAULT_REVIEW_FIGMA_REMOTE_UPLOAD_TIMEOUT_MS = 30000;\nconst FIGMA_TOKEN_STORAGE_KEY = 'figma-token';\n\ntype ReviewFigmaRemoteTokenProvider =\n | string\n | null\n | undefined\n | (() => string | null | undefined);\n\nexport type ReviewFigmaRemoteImageRow = {\n id: string;\n project_id: string;\n target_key: string;\n target: ReviewFigmaImageTarget;\n target_type: 'route' | 'figma-node';\n page_url: string | null;\n viewport_label: string | null;\n viewport_width: number | null;\n viewport_height: number | null;\n viewport_scope: 'mobile' | 'tablet' | 'desktop' | 'wide' | null;\n slot: string | null;\n figma_url: string;\n file_key: string;\n node_id: string;\n image_url: string;\n image_format: ReviewFigmaImageFormat;\n mime_type: string;\n storage_key: string;\n label: string | null;\n sort_order: number;\n width: number | null;\n height: number | null;\n byte_size: number | null;\n created_at: string;\n updated_at: string;\n};\n\ntype ReviewFigmaRemoteDbResult<T = unknown> = {\n data: T | null;\n error: unknown;\n};\n\ntype ReviewFigmaRemoteDbQuery<T = unknown> = PromiseLike<\n ReviewFigmaRemoteDbResult<T>\n> & {\n delete(): ReviewFigmaRemoteDbQuery<T>;\n eq(column: string, value: unknown): ReviewFigmaRemoteDbQuery<T>;\n insert(value: unknown): ReviewFigmaRemoteDbQuery<T>;\n order(\n column: string,\n options?: { ascending?: boolean }\n ): ReviewFigmaRemoteDbQuery<T>;\n select(columns?: string): ReviewFigmaRemoteDbQuery<T>;\n single(): PromiseLike<ReviewFigmaRemoteDbResult<T>>;\n update(value: unknown): ReviewFigmaRemoteDbQuery<T>;\n};\n\nexport type ReviewFigmaRemoteDbClient = {\n from(table: string): ReviewFigmaRemoteDbQuery;\n};\n\nexport type ReviewFigmaRemoteAssetUploadResponse = {\n storageKey?: unknown;\n r2Key?: unknown;\n imageUrl?: unknown;\n publicUrl?: unknown;\n error?: unknown;\n};\n\nexport type RemoteReviewFigmaImageStoreOptions = {\n client: ReviewFigmaRemoteDbClient;\n uploadEndpoint: string;\n table?: string;\n fetch?: typeof fetch;\n token?: ReviewFigmaRemoteTokenProvider;\n clientRender?: boolean | ReviewFigmaImageClientRenderOptions;\n uploadTimeoutMs?: number;\n};\n\nexport function createRemoteReviewFigmaImageStore({\n client,\n uploadEndpoint,\n table = DEFAULT_REVIEW_FIGMA_REMOTE_IMAGES_TABLE,\n fetch: fetchOption,\n token,\n clientRender = true,\n uploadTimeoutMs = DEFAULT_REVIEW_FIGMA_REMOTE_UPLOAD_TIMEOUT_MS,\n}: RemoteReviewFigmaImageStoreOptions): ReviewFigmaImageStore {\n const listImages = async (target: ReviewFigmaImageTarget) => {\n const targetKey = getReviewFigmaImageTargetKey(target);\n const { data, error } = await client\n .from(table)\n .select('*')\n .eq('project_id', target.projectId)\n .eq('target_key', targetKey)\n .order('sort_order', { ascending: true })\n .order('created_at', { ascending: true });\n\n if (error) throwRemoteStoreError(error, 'list Figma images');\n return ((data ?? []) as ReviewFigmaRemoteImageRow[]).map(rowToFigmaImage);\n };\n\n return {\n listImages,\n async addImage(input) {\n const ref = parseReviewFigmaNodeRef(input.figmaUrl);\n if (!ref) {\n throw new Error('A Figma node link or fileKey->nodeId value is required.');\n }\n\n const id = createFigmaImageId();\n const asset = await resolveFigmaImageAsset({\n input,\n token,\n clientRender,\n fetch: fetchOption,\n });\n const uploaded = await uploadFigmaAsset({\n asset,\n endpoint: uploadEndpoint,\n fetch: fetchOption,\n imageId: id,\n projectId: input.target.projectId,\n timeoutMs: uploadTimeoutMs,\n });\n const order =\n typeof input.order === 'number' && Number.isFinite(input.order)\n ? input.order\n : await getNextImageOrder(listImages, input.target);\n const now = new Date().toISOString();\n const row = createImageRow({\n asset,\n fileKey: ref.fileKey,\n id,\n imageUrl: uploaded.imageUrl,\n input,\n nodeId: ref.nodeId,\n now,\n order,\n storageKey: uploaded.storageKey,\n });\n const { data, error } = await client\n .from(table)\n .insert(row)\n .select('*')\n .single();\n\n if (error) throwRemoteStoreError(error, 'insert Figma image');\n return rowToFigmaImage(data as ReviewFigmaRemoteImageRow);\n },\n async updateImage(id, patch) {\n const { data, error } = await client\n .from(table)\n .update(createUpdatePatch(patch))\n .eq('id', id)\n .select('*')\n .single();\n\n if (error) throwRemoteStoreError(error, 'update Figma image');\n return rowToFigmaImage(data as ReviewFigmaRemoteImageRow);\n },\n async reorderImages(input) {\n const current = await listImages(input.target);\n const currentIds = new Set(current.map((image) => image.id));\n const nextIds = [\n ...input.imageIds.filter((id) => currentIds.has(id)),\n ...current\n .map((image) => image.id)\n .filter((id) => !input.imageIds.includes(id)),\n ];\n const targetKey = getReviewFigmaImageTargetKey(input.target);\n const updatedAt = new Date().toISOString();\n\n const results = await Promise.all(\n nextIds.map((id, index) =>\n client\n .from(table)\n .update({ sort_order: index, updated_at: updatedAt })\n .eq('id', id)\n .eq('project_id', input.target.projectId)\n .eq('target_key', targetKey)\n )\n );\n const failed = results.find((result) => result.error);\n if (failed?.error) throwRemoteStoreError(failed.error, 'reorder Figma images');\n\n return listImages(input.target);\n },\n async deleteImage(id) {\n const { error } = await client.from(table).delete().eq('id', id);\n if (error) throwRemoteStoreError(error, 'delete Figma image');\n },\n };\n}\n\nasync function resolveFigmaImageAsset({\n input,\n token,\n clientRender,\n fetch,\n}: {\n input: AddReviewFigmaImageInput;\n token: ReviewFigmaRemoteTokenProvider;\n clientRender: boolean | ReviewFigmaImageClientRenderOptions;\n fetch: typeof globalThis.fetch | undefined;\n}) {\n if (input.asset) return input.asset;\n if (!clientRender) {\n throw new Error('Figma image asset is required when clientRender is disabled.');\n }\n\n const renderOptions = clientRender === true ? {} : clientRender;\n const figmaToken = readRemoteFigmaToken(renderOptions.token ?? token);\n if (!figmaToken) throw new Error('Figma token is required.');\n\n return createReviewFigmaClientRenderedAsset({\n ...renderOptions,\n fetch,\n figmaUrl: input.figmaUrl,\n token: figmaToken,\n });\n}\n\nasync function uploadFigmaAsset({\n asset,\n endpoint,\n fetch: fetchOption,\n imageId,\n projectId,\n timeoutMs,\n}: {\n asset: ReviewFigmaImageAssetInput;\n endpoint: string;\n fetch: typeof globalThis.fetch | undefined;\n imageId: string;\n projectId: string;\n timeoutMs: number;\n}) {\n const requestFetch = fetchOption ?? globalThis.fetch;\n if (!requestFetch) throw new Error('Figma asset upload requires fetch.');\n\n const { blob, mimeType } = decodeAssetDataUrl(asset);\n const response = await withTimeout(\n requestFetch(\n createAssetUploadUrl(endpoint, {\n imageFormat: asset.imageFormat,\n imageId,\n projectId,\n }),\n {\n method: 'POST',\n headers: {\n 'Content-Type': mimeType,\n },\n body: blob,\n }\n ),\n timeoutMs,\n 'Figma asset upload timed out.'\n );\n const data = (await response.json().catch(() => null)) as\n | ReviewFigmaRemoteAssetUploadResponse\n | null;\n\n if (!response.ok) {\n throw new Error(\n readString(data?.error) || `Figma asset upload failed with ${response.status}`\n );\n }\n\n const storageKey = readString(data?.storageKey) ?? readString(data?.r2Key);\n const imageUrl = readString(data?.imageUrl) ?? readString(data?.publicUrl);\n if (!storageKey || !imageUrl) {\n throw new Error('Figma asset upload response is missing image URL.');\n }\n\n return { storageKey, imageUrl };\n}\n\nfunction createAssetUploadUrl(\n endpoint: string,\n params: {\n imageFormat: ReviewFigmaImageFormat;\n imageId: string;\n projectId: string;\n }\n) {\n const base =\n typeof window !== 'undefined' ? window.location.origin : 'http://localhost';\n const url = new URL(endpoint, base);\n url.searchParams.set('projectId', params.projectId);\n url.searchParams.set('imageId', params.imageId);\n url.searchParams.set('imageFormat', params.imageFormat);\n return url.toString();\n}\n\nfunction createImageRow({\n asset,\n fileKey,\n id,\n imageUrl,\n input,\n nodeId,\n now,\n order,\n storageKey,\n}: {\n asset: ReviewFigmaImageAssetInput;\n fileKey: string;\n id: string;\n imageUrl: string;\n input: AddReviewFigmaImageInput;\n nodeId: string;\n now: string;\n order: number;\n storageKey: string;\n}): ReviewFigmaRemoteImageRow {\n const target = input.target;\n const targetKey = getReviewFigmaImageTargetKey(target);\n\n return {\n id,\n project_id: target.projectId,\n target_key: targetKey,\n target,\n target_type: target.type,\n page_url: target.type === 'route' ? target.pageUrl : null,\n viewport_label: target.type === 'route' ? target.viewport?.label ?? null : null,\n viewport_width: target.type === 'route' ? target.viewport?.width ?? null : null,\n viewport_height: target.type === 'route' ? target.viewport?.height ?? null : null,\n viewport_scope: target.type === 'route' ? target.viewport?.scope ?? null : null,\n slot: target.type === 'route' ? target.slot ?? null : null,\n figma_url: input.figmaUrl,\n file_key: fileKey,\n node_id: nodeId,\n image_url: imageUrl,\n image_format: asset.imageFormat,\n mime_type: asset.mimeType,\n storage_key: storageKey,\n label: normalizeOptionalText(input.label) ?? null,\n sort_order: order,\n width: asset.width ?? null,\n height: asset.height ?? null,\n byte_size: asset.byteSize ?? null,\n created_at: now,\n updated_at: now,\n };\n}\n\nfunction createUpdatePatch(patch: UpdateReviewFigmaImageInput) {\n return {\n ...(patch.label !== undefined\n ? { label: normalizeOptionalText(patch.label) ?? null }\n : {}),\n ...(typeof patch.order === 'number' && Number.isFinite(patch.order)\n ? { sort_order: patch.order }\n : {}),\n updated_at: new Date().toISOString(),\n };\n}\n\nasync function getNextImageOrder(\n listImages: (target: ReviewFigmaImageTarget) => Promise<ReviewFigmaImage[]>,\n target: ReviewFigmaImageTarget\n) {\n const images = await listImages(target);\n return images.length\n ? Math.max(...images.map((image) => image.order)) + 1\n : 0;\n}\n\nfunction rowToFigmaImage(row: ReviewFigmaRemoteImageRow): ReviewFigmaImage {\n return {\n id: row.id,\n projectId: row.project_id,\n target: row.target,\n figmaUrl: row.figma_url,\n fileKey: row.file_key,\n nodeId: row.node_id,\n imageUrl: row.image_url,\n imageFormat: row.image_format,\n mimeType: row.mime_type,\n label: row.label ?? undefined,\n order: row.sort_order,\n storageKey: row.storage_key,\n width: row.width ?? undefined,\n height: row.height ?? undefined,\n byteSize: row.byte_size ?? undefined,\n createdAt: row.created_at,\n updatedAt: row.updated_at,\n };\n}\n\nfunction decodeAssetDataUrl(asset: ReviewFigmaImageAssetInput) {\n const match = asset.dataUrl.match(/^data:([^;,]+);base64,(.*)$/);\n if (!match) throw new Error('Valid Figma image asset data URL is required.');\n\n const mimeType = match[1]?.trim();\n if (!mimeType || mimeType !== asset.mimeType) {\n throw new Error('Figma image asset MIME type mismatch.');\n }\n\n const binary = globalThis.atob(match[2] ?? '');\n const bytes = new Uint8Array(binary.length);\n for (let index = 0; index < binary.length; index += 1) {\n bytes[index] = binary.charCodeAt(index);\n }\n\n return {\n blob: new Blob([bytes], { type: mimeType }),\n mimeType,\n };\n}\n\nasync function withTimeout<T>(\n promise: Promise<T>,\n timeoutMs: number,\n message: string\n) {\n let timeoutId: ReturnType<typeof setTimeout> | undefined;\n try {\n return await Promise.race([\n promise,\n new Promise<T>((_, reject) => {\n timeoutId = setTimeout(() => reject(new Error(message)), timeoutMs);\n }),\n ]);\n } finally {\n if (timeoutId) clearTimeout(timeoutId);\n }\n}\n\nfunction readRemoteFigmaToken(provider: ReviewFigmaRemoteTokenProvider) {\n const token = typeof provider === 'function' ? provider() : provider;\n if (typeof token === 'string' && token.trim()) return token.trim();\n return readStoredFigmaToken();\n}\n\nfunction readStoredFigmaToken() {\n if (typeof window === 'undefined') return '';\n\n try {\n return window.localStorage.getItem(FIGMA_TOKEN_STORAGE_KEY)?.trim() || '';\n } catch {\n return '';\n }\n}\n\nfunction createFigmaImageId() {\n const id =\n globalThis.crypto?.randomUUID?.() ?? Math.random().toString(36).slice(2);\n return `figma_${id.replace(/[^A-Za-z0-9_-]+/g, '-')}`;\n}\n\nfunction normalizeOptionalText(value: string | null | undefined) {\n if (typeof value !== 'string') return undefined;\n return value.trim() || undefined;\n}\n\nfunction readString(value: unknown) {\n return typeof value === 'string' && value.trim() ? value.trim() : null;\n}\n\nfunction throwRemoteStoreError(error: unknown, action: string): never {\n const source = error as {\n code?: unknown;\n details?: unknown;\n hint?: unknown;\n message?: unknown;\n };\n const message = readString(source.message) ?? `Remote Figma image ${action} failed.`;\n const code = readString(source.code);\n const details = readString(source.details);\n const hint = readString(source.hint);\n const suffix = [code, details, hint].filter(Boolean).join(' / ');\n\n throw new Error(suffix ? `${message} (${suffix})` : message);\n}\n"],"mappings":"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","names":[]}
|