{"version":3,"sources":["../src/metadata-image.ts"],"sourcesContent":["import type { ImageResponseOptions } from \"@vercel/og\";\nimport type { ReactElement } from \"react\";\nimport { omitFarmResponseBody } from \"./response-body\";\nimport { matchesFarmIfNoneMatch } from \"./server-http\";\n\nconst REACT_ELEMENT_TYPE = Symbol.for(\"react.element\");\nconst REACT_TRANSITIONAL_ELEMENT_TYPE = Symbol.for(\"react.transitional.element\");\nconst REACT_FORWARD_REF_TYPE = Symbol.for(\"react.forward_ref\");\nconst REACT_MEMO_TYPE = Symbol.for(\"react.memo\");\nconst REACT_LAZY_TYPE = Symbol.for(\"react.lazy\");\n\ntype MetadataImageSize = {\n  width?: number;\n  height?: number;\n};\n\ntype MetadataImageFont = Omit<NonNullable<ImageResponseOptions[\"fonts\"]>[number], \"data\"> & {\n  data: ArrayBuffer | ArrayBufferView | Promise<ArrayBuffer | ArrayBufferView>;\n};\n\nexport type FarmMetadataImageModule = {\n  size?: MetadataImageSize;\n  contentType?: string;\n  revalidate?: number | false;\n  fonts?: MetadataImageFont[];\n  emoji?: ImageResponseOptions[\"emoji\"];\n  debug?: boolean;\n};\n\nexport type FarmMetadataImageResponseOptions = {\n  method?: string;\n  ifNoneMatch?: string | null;\n};\n\nfunction isResponse(value: unknown): value is Response {\n  return (\n    typeof Response !== \"undefined\" &&\n    (value instanceof Response ||\n      Boolean(\n        value &&\n        typeof value === \"object\" &&\n        typeof (value as Response).arrayBuffer === \"function\" &&\n        (value as Response).headers,\n      ))\n  );\n}\n\nfunction isThenable(value: unknown): value is PromiseLike<unknown> {\n  return Boolean(\n    value &&\n    (typeof value === \"object\" || typeof value === \"function\") &&\n    typeof (value as PromiseLike<unknown>).then === \"function\",\n  );\n}\n\nfunction isReactElement(value: unknown): value is ReactElement<Record<string, unknown>> {\n  if (!value || typeof value !== \"object\") return false;\n  const marker = (value as { $$typeof?: symbol }).$$typeof;\n  return marker === REACT_ELEMENT_TYPE || marker === REACT_TRANSITIONAL_ELEMENT_TYPE;\n}\n\nasync function prepareMetadataImageNode(node: unknown): Promise<unknown> {\n  if (isThenable(node)) {\n    return prepareMetadataImageNode(await node);\n  }\n\n  if (Array.isArray(node)) {\n    return Promise.all(node.map((child) => prepareMetadataImageNode(child)));\n  }\n\n  if (!isReactElement(node)) {\n    return node;\n  }\n\n  const element = node;\n  const type = element.type as any;\n  const props = element.props || {};\n\n  if (typeof type === \"function\") {\n    if (type.prototype?.isReactComponent) {\n      throw new Error(\n        \"Metadata image components must be stateless function components; React class components are not supported.\",\n      );\n    }\n    return prepareMetadataImageNode(await type(props));\n  }\n\n  if (type && typeof type === \"object\") {\n    if (type.$$typeof === REACT_MEMO_TYPE) {\n      const { createElement } = await import(\"react\");\n      return prepareMetadataImageNode(createElement(type.type, props));\n    }\n    if (type.$$typeof === REACT_FORWARD_REF_TYPE) {\n      return prepareMetadataImageNode(await type.render(props, null));\n    }\n    if (type.$$typeof === REACT_LAZY_TYPE) {\n      const { createElement } = await import(\"react\");\n      let resolvedType: unknown;\n      try {\n        resolvedType = type._init(type._payload);\n      } catch (suspension) {\n        if (suspension && typeof (suspension as Promise<unknown>).then === \"function\") {\n          await suspension;\n          resolvedType = type._init(type._payload);\n        } else {\n          throw suspension;\n        }\n      }\n      return prepareMetadataImageNode(createElement(resolvedType as any, props));\n    }\n  }\n\n  const preparedChildren = await prepareMetadataImageNode(props.children);\n  const preparedProps: Record<string, unknown> = { ...props };\n\n  if (typeof preparedProps.className === \"string\") {\n    preparedProps.tw = [preparedProps.tw, preparedProps.className].filter(Boolean).join(\" \");\n    delete preparedProps.className;\n  }\n  delete preparedProps.children;\n\n  const { createElement } = await import(\"react\");\n  return createElement(type, {\n    ...preparedProps,\n    key: element.key,\n    children: preparedChildren,\n  });\n}\n\nasync function normalizeFonts(\n  fonts: MetadataImageFont[] | undefined,\n): Promise<ImageResponseOptions[\"fonts\"] | undefined> {\n  if (!fonts?.length) return undefined;\n\n  return Promise.all(\n    fonts.map(async (font) => {\n      const data = await font.data;\n      const arrayBuffer = ArrayBuffer.isView(data) ? toArrayBuffer(data) : data;\n      return { ...font, data: arrayBuffer } as NonNullable<ImageResponseOptions[\"fonts\"]>[number];\n    }),\n  );\n}\n\nfunction resolveCacheControl(revalidate: number | false | undefined): string {\n  if (revalidate === false) {\n    return \"public, max-age=31536000, immutable\";\n  }\n  if (typeof revalidate === \"number\" && Number.isFinite(revalidate) && revalidate > 0) {\n    return `public, s-maxage=${Math.floor(revalidate)}, stale-while-revalidate=300`;\n  }\n  return \"public, max-age=0, must-revalidate\";\n}\n\nfunction toArrayBuffer(value: ArrayBuffer | ArrayBufferView): ArrayBuffer {\n  if (value instanceof ArrayBuffer) return value;\n  const bytes = new Uint8Array(value.byteLength);\n  bytes.set(new Uint8Array(value.buffer, value.byteOffset, value.byteLength));\n  return bytes.buffer;\n}\n\nasync function createEntityTag(body: ArrayBuffer): Promise<string> {\n  const digest = await globalThis.crypto.subtle.digest(\"SHA-256\", body);\n  const hash = Array.from(new Uint8Array(digest), (byte) => byte.toString(16).padStart(2, \"0\"))\n    .join(\"\")\n    .slice(0, 32);\n  return `\"${hash}\"`;\n}\n\nasync function finalizeMetadataImageResponse(\n  body: ArrayBuffer,\n  headers: HeadersInit,\n  options: FarmMetadataImageResponseOptions,\n): Promise<Response> {\n  const responseHeaders = new Headers(headers);\n  const etag = await createEntityTag(body);\n  responseHeaders.set(\"ETag\", etag);\n  responseHeaders.set(\"Content-Length\", String(body.byteLength));\n  responseHeaders.set(\"X-Content-Type-Options\", \"nosniff\");\n\n  if (matchesFarmIfNoneMatch(options.ifNoneMatch, etag)) {\n    return new Response(null, { status: 304, headers: responseHeaders });\n  }\n\n  return new Response(options.method?.toUpperCase() === \"HEAD\" ? null : body, {\n    status: 200,\n    headers: responseHeaders,\n  });\n}\n\n/** @internal */\nexport async function createFarmMetadataImageResponse(\n  value: unknown,\n  imageModule: FarmMetadataImageModule,\n  options: FarmMetadataImageResponseOptions = {},\n): Promise<Response> {\n  const method = (options.method || \"GET\").toUpperCase();\n  if (method !== \"GET\" && method !== \"HEAD\") {\n    return new Response(null, { status: 405, headers: { Allow: \"GET, HEAD\" } });\n  }\n\n  if (isResponse(value)) {\n    return method === \"HEAD\" ? omitFarmResponseBody(value) : value;\n  }\n\n  const cacheControl = resolveCacheControl(imageModule.revalidate);\n  const explicitContentType = imageModule.contentType?.split(\";\", 1)[0]?.trim().toLowerCase();\n\n  if (isReactElement(value) && explicitContentType !== \"image/svg+xml\") {\n    const { ImageResponse } = await import(\"@vercel/og\");\n    const element = (await prepareMetadataImageNode(value)) as ReactElement;\n    const response = new ImageResponse(element, {\n      width: imageModule.size?.width || 1200,\n      height: imageModule.size?.height || 630,\n      fonts: await normalizeFonts(imageModule.fonts),\n      emoji: imageModule.emoji,\n      debug: imageModule.debug,\n      headers: { \"cache-control\": cacheControl },\n    });\n    const headers = new Headers(response.headers);\n    headers.set(\"Cache-Control\", cacheControl);\n    return finalizeMetadataImageResponse(await response.arrayBuffer(), headers, options);\n  }\n\n  let body: ArrayBuffer;\n  if (typeof value === \"string\") {\n    body = toArrayBuffer(new TextEncoder().encode(value));\n  } else if (value instanceof ArrayBuffer || ArrayBuffer.isView(value)) {\n    body = toArrayBuffer(value);\n  } else if (isReactElement(value)) {\n    const { renderToStaticMarkup } = await import(\"react-dom/server\");\n    body = toArrayBuffer(new TextEncoder().encode(renderToStaticMarkup(value)));\n  } else {\n    throw new Error(\"Metadata image must return a Response, string, bytes, or React element\");\n  }\n\n  return finalizeMetadataImageResponse(\n    body,\n    {\n      \"Content-Type\": imageModule.contentType || \"image/svg+xml; charset=utf-8\",\n      \"Cache-Control\": cacheControl,\n    },\n    options,\n  );\n}\n"],"mappings":"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","names":["createElement"]}