@alihaiderrana/email-builder-sdk 0.1.14 → 0.1.16
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.cjs +1 -1
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +18 -1
- package/dist/index.d.ts +18 -1
- package/dist/index.js +1 -1
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.cjs
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
"use strict";var
|
|
1
|
+
"use strict";var $=Object.defineProperty;var Me=Object.getOwnPropertyDescriptor;var Le=Object.getOwnPropertyNames;var Ie=Object.prototype.hasOwnProperty;var De=(a,n)=>{for(var t in n)$(a,t,{get:n[t],enumerable:!0})},Pe=(a,n,t,p)=>{if(n&&typeof n=="object"||typeof n=="function")for(let y of Le(n))!Ie.call(a,y)&&y!==t&&$(a,y,{get:()=>n[y],enumerable:!(p=Me(n,y))||p.enumerable});return a};var Ue=a=>Pe($({},"__esModule",{value:!0}),a);var ve={};De(ve,{EMAIL_BUILDER_PROTOCOL_VERSION:()=>ie,EmailBuilder:()=>ke,createMessageMeta:()=>j,isMessageLike:()=>H});module.exports=Ue(ve);var r=require("react");var ie="1.0.0",_e=["INIT","READY","CHANGE","LOADED","SAVE","UPLOAD","UPLOAD_SUCCESS","LIST_ASSETS","ASSETS_LIST","DELETE_ASSET","DELETE_ASSET_SUCCESS","AUTH_ERROR"];function H(a){if(typeof a!="object"||a===null)return!1;let n=a;if(typeof n.type!="string"||!_e.includes(n.type))return!1;if("meta"in n&&n.meta!==void 0){let t=n.meta;if(t.id&&typeof t.id!="string"||t.correlationId&&typeof t.correlationId!="string"||t.version&&typeof t.version!="string"||t.sentAt&&typeof t.sentAt!="number")return!1}return!0}function j(a){return{id:typeof crypto!="undefined"&&"randomUUID"in crypto?crypto.randomUUID():Math.random().toString(36).slice(2),correlationId:a,version:ie,sentAt:Date.now()}}function oe(a,n){if(n){let p=new URL(n);if(p.origin==="null")throw new Error("allowedOrigin must resolve to a valid origin");return p.origin}let t;try{t=new URL(a)}catch{throw new Error("EmailBuilder `src` must be an absolute URL (e.g. https://example.com)")}if(!t.origin||t.origin==="null")throw new Error("EmailBuilder requires an absolute src URL with a valid origin");return t.origin}function L(a,n){let t=j(n);return{...a,meta:t}}function z(a){return JSON.stringify(a!=null?a:null)}var I=require("react/jsx-runtime"),Re="allow-scripts allow-same-origin allow-forms",he="<h1>Hello World</h1><p>Start building your email template.</p>",Be="https://pc-email-template-builder.netlify.app",be={position:"absolute",inset:0,display:"flex",alignItems:"center",justifyContent:"center",fontSize:"0.875rem",fontWeight:500,background:"linear-gradient(135deg, rgba(9,9,9,0.65), rgba(33,33,33,0.85))",color:"#fff",zIndex:2},Oe={border:"none",width:"100%",height:"100%"};function Ce(a,n){if(!n)return a;try{let t=new URL(a,typeof window!="undefined"?window.location.href:"https://example.com");return t.searchParams.set("preview","true"),t.searchParams.set("previewOnly","true"),t.toString()}catch{let t=a.includes("?")?"&":"?";return`${a}${t}preview=true&previewOnly=true`}}function xe(a,n){if(!n||!n.trim())return a;try{let t=new URL(a,typeof window!="undefined"?window.location.href:"https://example.com");return t.searchParams.set("embedToken",n),t.toString()}catch{let t=a.includes("?")?"&":"?";return`${a}${t}embedToken=${encodeURIComponent(n)}`}}function He({src:a,initialHtml:n,mergeTags:t,mergeTagTrigger:p,embedToken:y,templateId:T,config:w,showFooter:k,includeUnsubscribe:v,externalFooterHtml:g,footerInjectionMode:q,preview:le=!1,previewOnly:ue=!1,className:ce,style:de,hideLoadingOverlay:pe=!1,iframeTitle:fe="Email Builder",sandbox:Se=Re,onChange:D,onLoad:P,onSave:U,onUpload:N,onListAssets:W,onDeleteAsset:V,onReady:_,onStatusChange:R,onAuthError:E,allowedOrigin:F},ye){let h=(a==null?void 0:a.trim())||Be,B=le||ue,K=(0,r.useMemo)(()=>B?{...w!=null?w:{},preview:!0,previewOnly:!0}:w,[w,B]),Ee=(0,r.useMemo)(()=>xe(Ce(h,B),y),[h,y,B]),b=(0,r.useRef)(null),d=(0,r.useRef)(null),S=(0,r.useRef)(!1),O=(0,r.useRef)("loading"),[me,J]=(0,r.useState)("loading"),[ge,Te]=(0,r.useState)(0),C=(0,r.useRef)([]),A=(0,r.useRef)(null),f=(0,r.useRef)(null),i=(0,r.useRef)(null),Y=typeof n=="string"?n:T?"":he,Q=q||(g&&g.trim()?"sdk":"default"),X={html:Y,...Array.isArray(t)?{mergeTags:t}:{},...typeof p=="string"&&p.trim()?{mergeTagTrigger:p.trim()}:{},config:K,...T?{templateId:T}:{},showFooter:k,includeUnsubscribe:v,...g?{externalFooterHtml:g}:{},footerInjectionMode:Q},Z=(0,r.useRef)(z(X)),M=(0,r.useRef)({type:"INIT",payload:X}),Ne=(0,r.useMemo)(()=>oe(h,F),[h,F]),x=(0,r.useCallback)(()=>"*",[]),m=(0,r.useCallback)(e=>{O.current!==e&&(O.current=e,J(e),R==null||R(e))},[R]),c=(0,r.useCallback)((e,o)=>{var l,u;let s=(u=(l=b.current)==null?void 0:l.contentWindow)!=null?u:d.current;if(s&&(d.current=s),!d.current||!S.current){C.current.push({message:e,correlationId:o});return}d.current.postMessage(L(e,o),x())},[x]),ee=(0,r.useCallback)(()=>{if(!S.current||!d.current)return;let e=[...C.current];C.current=[],e.forEach(({message:o,correlationId:s})=>{var l;(l=d.current)==null||l.postMessage(L(o,s),x())})},[x]),te=(0,r.useCallback)(()=>{S.current||(f.current&&(window.clearTimeout(f.current),f.current=null),i.current&&(window.clearInterval(i.current),i.current=null),S.current=!0,m("ready"),_==null||_(),c(M.current),ee())},[ee,_,c,m]),re=(0,r.useCallback)(async e=>{var o,s;if(e.type==="UPLOAD"){if(!N){console.warn("[EmailBuilderSDK] Upload requested but no onUpload handler is configured.");return}try{let l=await N(e.payload.file);c({type:"UPLOAD_SUCCESS",payload:{url:l}},(o=e.meta)==null?void 0:o.id)}catch(l){console.error("[EmailBuilderSDK] Upload handler failed",l),c({type:"UPLOAD_SUCCESS",payload:{url:""}},(s=e.meta)==null?void 0:s.id)}}},[N,c]),ae=(0,r.useCallback)(async e=>{var o,s,l;if(e.type==="LIST_ASSETS"){if(!W){c({type:"ASSETS_LIST",payload:{assets:[]}},(o=e.meta)==null?void 0:o.id);return}try{let u=await W(e.payload);c({type:"ASSETS_LIST",payload:{assets:Array.isArray(u)?u:[]}},(s=e.meta)==null?void 0:s.id)}catch(u){console.error("[EmailBuilderSDK] list assets handler failed",u),c({type:"ASSETS_LIST",payload:{assets:[]}},(l=e.meta)==null?void 0:l.id)}}},[W,c]),ne=(0,r.useCallback)(async e=>{var o,s,l;if(e.type==="DELETE_ASSET"){if(!V){c({type:"DELETE_ASSET_SUCCESS",payload:{success:!1}},(o=e.meta)==null?void 0:o.id);return}try{let u=await V(e.payload||{});c({type:"DELETE_ASSET_SUCCESS",payload:{success:!!u}},(s=e.meta)==null?void 0:s.id)}catch(u){console.error("[EmailBuilderSDK] delete asset handler failed",u),c({type:"DELETE_ASSET_SUCCESS",payload:{success:!1}},(l=e.meta)==null?void 0:l.id)}}},[V,c]),G=(0,r.useCallback)(e=>{var l,u,se;let o=(u=(l=b.current)==null?void 0:l.contentWindow)!=null?u:d.current;if(!o||e.source!==o||!H(e.data))return;if(!A.current)A.current=e.origin;else if(e.origin!==A.current)return;let s=e.data;switch(e.source&&e.source!==d.current&&(d.current=e.source),s.type){case"READY":te();break;case"CHANGE":D==null||D(s.payload.html);break;case"LOADED":P==null||P(s.payload.html);break;case"SAVE":U==null||U(s.payload.html);break;case"UPLOAD":re(s);break;case"LIST_ASSETS":ae(s);break;case"DELETE_ASSET":ne(s);break;case"AUTH_ERROR":{let Ae=((se=s.payload)==null?void 0:se.message)||"Email builder authentication failed";O.current!=="error"&&(O.current="error",J("error"),E==null||E(Ae));break}default:break}},[te,re,ae,ne,D,P,U,E,m]);(0,r.useEffect)(()=>{if(typeof window!="undefined")return window.addEventListener("message",G),()=>{window.removeEventListener("message",G)}},[G]),(0,r.useEffect)(()=>{let e={html:Y,...Array.isArray(t)?{mergeTags:t}:{},...typeof p=="string"&&p.trim()?{mergeTagTrigger:p.trim()}:{},config:K,...T?{templateId:T}:{},showFooter:k,includeUnsubscribe:v,...g!==void 0?{externalFooterHtml:g}:{},footerInjectionMode:Q},o=z(e);M.current={type:"INIT",payload:e},o!==Z.current&&S.current&&c(M.current),Z.current=o},[K,Y,t,p,c,T,k,v,g,q]);let we=(0,r.useCallback)(()=>{var s,l;if(d.current=(l=(s=b.current)==null?void 0:s.contentWindow)!=null?l:null,S.current=!1,A.current=null,m("loading"),d.current)try{d.current.postMessage(L(M.current),"*")}catch{}f.current&&window.clearTimeout(f.current),i.current&&window.clearInterval(i.current);let e=0,o=12;i.current=window.setInterval(()=>{var u;if(S.current){i.current&&(window.clearInterval(i.current),i.current=null);return}e+=1;try{(u=d.current)==null||u.postMessage(L(M.current),"*")}catch{}e>=o&&i.current&&(window.clearInterval(i.current),i.current=null)},1e3),f.current=window.setTimeout(()=>{S.current||(i.current&&(window.clearInterval(i.current),i.current=null),m("error"),E==null||E("Builder handshake failed or authentication was rejected."))},12e3)},[E,m]);return(0,r.useImperativeHandle)(ye,()=>({reload(){C.current=[],S.current=!1,d.current=null,A.current=null,f.current&&(window.clearTimeout(f.current),f.current=null),i.current&&(window.clearInterval(i.current),i.current=null),m("loading"),Te(e=>e+1)}}),[m]),(0,r.useEffect)(()=>()=>{f.current&&(window.clearTimeout(f.current),f.current=null),i.current&&(window.clearInterval(i.current),i.current=null)},[]),(0,I.jsxs)("div",{className:ce,style:{position:"relative",width:"100%",height:"100%",...de},children:[(0,I.jsx)("iframe",{ref:b,src:Ee,title:fe,sandbox:Se,style:Oe,loading:"lazy",allowFullScreen:!0,onLoad:we},ge),!pe&&me!=="ready"&&(0,I.jsx)("div",{style:be,children:"Connecting to builder\u2026"})]})}var ke=(0,r.forwardRef)(He);0&&(module.exports={EMAIL_BUILDER_PROTOCOL_VERSION,EmailBuilder,createMessageMeta,isMessageLike});
|
|
2
2
|
//# sourceMappingURL=index.cjs.map
|
package/dist/index.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/index.ts","../src/EmailBuilder.tsx","../src/protocol.ts","../src/utils.ts"],"sourcesContent":["export * from './EmailBuilder';\nexport * from './types';\nexport * from './protocol';\n","import React, {\n CSSProperties,\n ForwardedRef,\n forwardRef,\n useCallback,\n useEffect,\n useImperativeHandle,\n useMemo,\n useRef,\n useState,\n} from 'react';\nimport type { BuilderToHostMessage, HostToBuilderMessage, InitPayload } from './protocol';\nimport { isMessageLike } from './protocol';\nimport { buildEnvelope, deriveAllowedOrigin, stableSignature } from './utils';\nimport type { EmailBuilderHandle, EmailBuilderProps, EmailBuilderStatus } from './types';\n\nconst DEFAULT_SANDBOX = 'allow-scripts allow-same-origin allow-forms';\nconst DEFAULT_INITIAL_HTML = '<h1>Hello World</h1><p>Start building your email template.</p>';\nconst DEFAULT_BUILDER_SRC = 'https://pc-email-template-builder.netlify.app';\n\ntype PendingMessage = {\n message: HostToBuilderMessage;\n correlationId?: string;\n};\n\nconst overlayStyle: CSSProperties = {\n position: 'absolute',\n inset: 0,\n display: 'flex',\n alignItems: 'center',\n justifyContent: 'center',\n fontSize: '0.875rem',\n fontWeight: 500,\n background: 'linear-gradient(135deg, rgba(9,9,9,0.65), rgba(33,33,33,0.85))',\n color: '#fff',\n zIndex: 2,\n};\n\nconst iframeStyle: CSSProperties = {\n border: 'none',\n width: '100%',\n height: '100%',\n};\n\nfunction appendPreviewParamsToSrc(src: string, preview: boolean): string {\n if (!preview) {\n return src;\n }\n try {\n const u = new URL(src, typeof window !== 'undefined' ? window.location.href : 'https://example.com');\n u.searchParams.set('preview', 'true');\n u.searchParams.set('previewOnly', 'true');\n return u.toString();\n } catch {\n const sep = src.includes('?') ? '&' : '?';\n return `${src}${sep}preview=true&previewOnly=true`;\n }\n}\n\nfunction appendEmbedTokenToSrc(src: string, embedToken: string | undefined): string {\n if (!embedToken || !embedToken.trim()) {\n return src;\n }\n try {\n const u = new URL(src, typeof window !== 'undefined' ? window.location.href : 'https://example.com');\n u.searchParams.set('embedToken', embedToken);\n return u.toString();\n } catch {\n const sep = src.includes('?') ? '&' : '?';\n return `${src}${sep}embedToken=${encodeURIComponent(embedToken)}`;\n }\n}\n\nfunction EmailBuilderInner(\n {\n src,\n initialHtml,\n embedToken,\n templateId,\n config,\n showFooter,\n includeUnsubscribe,\n externalFooterHtml,\n footerInjectionMode,\n preview = false,\n previewOnly = false,\n className,\n style,\n iframeTitle = 'Email Builder',\n sandbox = DEFAULT_SANDBOX,\n onChange,\n onLoad,\n onSave,\n onUpload,\n onListAssets,\n onDeleteAsset,\n onReady,\n onStatusChange,\n onAuthError,\n allowedOrigin,\n }: EmailBuilderProps,\n ref: ForwardedRef<EmailBuilderHandle>\n) {\n const resolvedSrc = src?.trim() || DEFAULT_BUILDER_SRC;\n const isPreviewMode = preview || previewOnly;\n const effectiveConfig = useMemo(\n () => (isPreviewMode ? { ...(config ?? {}), preview: true, previewOnly: true } : config),\n [config, isPreviewMode]\n );\n const iframeSrc = useMemo(\n () => appendEmbedTokenToSrc(appendPreviewParamsToSrc(resolvedSrc, isPreviewMode), embedToken),\n [resolvedSrc, embedToken, isPreviewMode]\n );\n\n const iframeRef = useRef<HTMLIFrameElement>(null);\n const builderWindowRef = useRef<Window | null>(null);\n const readyRef = useRef(false);\n const statusRef = useRef<EmailBuilderStatus>('loading');\n const [status, setStatus] = useState<EmailBuilderStatus>('loading');\n const [reloadKey, setReloadKey] = useState(0);\n const queueRef = useRef<PendingMessage[]>([]);\n const runtimeOriginRef = useRef<string | null>(null);\n const handshakeTimerRef = useRef<number | null>(null);\n const handshakeRetryIntervalRef = useRef<number | null>(null);\n\n const effectiveInitialHtml =\n typeof initialHtml === 'string'\n ? initialHtml\n : templateId\n ? ''\n : DEFAULT_INITIAL_HTML;\n\n const effectiveFooterMode =\n footerInjectionMode ||\n (externalFooterHtml && externalFooterHtml.trim() ? 'sdk' : 'default');\n\n const initPayload: InitPayload = {\n html: effectiveInitialHtml,\n config: effectiveConfig,\n ...(templateId ? { templateId } : {}),\n showFooter,\n includeUnsubscribe,\n ...(externalFooterHtml ? { externalFooterHtml } : {}),\n footerInjectionMode: effectiveFooterMode,\n };\n\n const initSignatureRef = useRef(stableSignature(initPayload));\n const latestInitRef = useRef<HostToBuilderMessage>({\n type: 'INIT',\n payload: initPayload,\n });\n\n const expectedOrigin = useMemo(() => deriveAllowedOrigin(resolvedSrc, allowedOrigin), [resolvedSrc, allowedOrigin]);\n\n const resolveTargetOrigin = useCallback(() => '*', []);\n\n const setStatusSafely = useCallback(\n (next: EmailBuilderStatus) => {\n if (statusRef.current === next) {\n return;\n }\n statusRef.current = next;\n setStatus(next);\n onStatusChange?.(next);\n },\n [onStatusChange]\n );\n\n const postMessage = useCallback(\n (message: HostToBuilderMessage, correlationId?: string) => {\n const target = iframeRef.current?.contentWindow ?? builderWindowRef.current;\n if (target) {\n builderWindowRef.current = target;\n }\n\n if (!builderWindowRef.current || !readyRef.current) {\n queueRef.current.push({ message, correlationId });\n return;\n }\n\n builderWindowRef.current.postMessage(buildEnvelope(message, correlationId), resolveTargetOrigin());\n },\n [resolveTargetOrigin]\n );\n\n const flushQueue = useCallback(() => {\n if (!readyRef.current || !builderWindowRef.current) {\n return;\n }\n const pending = [...queueRef.current];\n queueRef.current = [];\n pending.forEach(({ message, correlationId }) => {\n builderWindowRef.current?.postMessage(\n buildEnvelope(message, correlationId),\n resolveTargetOrigin()\n );\n });\n }, [resolveTargetOrigin]);\n\n const handleReadyMessage = useCallback(() => {\n if (readyRef.current) {\n return;\n }\n if (handshakeTimerRef.current) {\n window.clearTimeout(handshakeTimerRef.current);\n handshakeTimerRef.current = null;\n }\n if (handshakeRetryIntervalRef.current) {\n window.clearInterval(handshakeRetryIntervalRef.current);\n handshakeRetryIntervalRef.current = null;\n }\n readyRef.current = true;\n setStatusSafely('ready');\n onReady?.();\n postMessage(latestInitRef.current);\n flushQueue();\n }, [flushQueue, onReady, postMessage, setStatusSafely]);\n\n const handleUpload = useCallback(\n async (eventMessage: BuilderToHostMessage) => {\n if (eventMessage.type !== 'UPLOAD') {\n return;\n }\n if (!onUpload) {\n console.warn('[EmailBuilderSDK] Upload requested but no onUpload handler is configured.');\n return;\n }\n try {\n const url = await onUpload(eventMessage.payload.file);\n postMessage({ type: 'UPLOAD_SUCCESS', payload: { url } }, eventMessage.meta?.id);\n } catch (error) {\n console.error('[EmailBuilderSDK] Upload handler failed', error);\n // Keep builder session ready; upload failure should not flip global iframe status.\n postMessage({ type: 'UPLOAD_SUCCESS', payload: { url: '' } }, eventMessage.meta?.id);\n }\n },\n [onUpload, postMessage]\n );\n\n const handleListAssets = useCallback(\n async (eventMessage: BuilderToHostMessage) => {\n if (eventMessage.type !== 'LIST_ASSETS') {\n return;\n }\n if (!onListAssets) {\n postMessage({ type: 'ASSETS_LIST', payload: { assets: [] } }, eventMessage.meta?.id);\n return;\n }\n try {\n const assets = await onListAssets(eventMessage.payload);\n postMessage(\n { type: 'ASSETS_LIST', payload: { assets: Array.isArray(assets) ? assets : [] } },\n eventMessage.meta?.id\n );\n } catch (error) {\n console.error('[EmailBuilderSDK] list assets handler failed', error);\n postMessage({ type: 'ASSETS_LIST', payload: { assets: [] } }, eventMessage.meta?.id);\n }\n },\n [onListAssets, postMessage]\n );\n\n const handleDeleteAsset = useCallback(\n async (eventMessage: BuilderToHostMessage) => {\n if (eventMessage.type !== 'DELETE_ASSET') {\n return;\n }\n if (!onDeleteAsset) {\n postMessage({ type: 'DELETE_ASSET_SUCCESS', payload: { success: false } }, eventMessage.meta?.id);\n return;\n }\n try {\n const success = await onDeleteAsset(eventMessage.payload || {});\n postMessage({ type: 'DELETE_ASSET_SUCCESS', payload: { success: !!success } }, eventMessage.meta?.id);\n } catch (error) {\n console.error('[EmailBuilderSDK] delete asset handler failed', error);\n postMessage({ type: 'DELETE_ASSET_SUCCESS', payload: { success: false } }, eventMessage.meta?.id);\n }\n },\n [onDeleteAsset, postMessage]\n );\n\n const handleMessage = useCallback(\n (event: MessageEvent) => {\n const iframeWindow = iframeRef.current?.contentWindow ?? builderWindowRef.current;\n if (!iframeWindow || event.source !== iframeWindow) {\n return;\n }\n if (!isMessageLike(event.data)) {\n return;\n }\n if (!runtimeOriginRef.current) {\n runtimeOriginRef.current = event.origin;\n } else if (event.origin !== runtimeOriginRef.current) {\n return;\n }\n const message = event.data as BuilderToHostMessage;\n\n if (event.source && event.source !== builderWindowRef.current) {\n builderWindowRef.current = event.source as Window;\n }\n\n switch (message.type) {\n case 'READY':\n handleReadyMessage();\n break;\n case 'CHANGE':\n onChange?.(message.payload.html);\n break;\n case 'LOADED':\n onLoad?.(message.payload.html);\n break;\n case 'SAVE':\n onSave?.(message.payload.html);\n break;\n case 'UPLOAD':\n void handleUpload(message);\n break;\n case 'LIST_ASSETS':\n void handleListAssets(message);\n break;\n case 'DELETE_ASSET':\n void handleDeleteAsset(message);\n break;\n case 'AUTH_ERROR': {\n const msg = message.payload?.message || 'Email builder authentication failed';\n if (statusRef.current !== 'error') {\n statusRef.current = 'error';\n setStatus('error');\n onAuthError?.(msg);\n }\n break;\n }\n default:\n break;\n }\n },\n [handleReadyMessage, handleUpload, handleListAssets, handleDeleteAsset, onChange, onLoad, onSave, onAuthError, setStatusSafely]\n );\n\n useEffect(() => {\n if (typeof window === 'undefined') {\n return;\n }\n window.addEventListener('message', handleMessage);\n return () => {\n window.removeEventListener('message', handleMessage);\n };\n }, [handleMessage]);\n\n useEffect(() => {\n const nextPayload: InitPayload = {\n html: effectiveInitialHtml,\n config: effectiveConfig,\n ...(templateId ? { templateId } : {}),\n showFooter,\n includeUnsubscribe,\n ...(externalFooterHtml !== undefined ? { externalFooterHtml } : {}),\n footerInjectionMode: effectiveFooterMode,\n };\n const signature = stableSignature(nextPayload);\n latestInitRef.current = { type: 'INIT', payload: nextPayload };\n if (signature !== initSignatureRef.current && readyRef.current) {\n postMessage(latestInitRef.current);\n }\n initSignatureRef.current = signature;\n }, [\n effectiveConfig,\n effectiveInitialHtml,\n postMessage,\n templateId,\n showFooter,\n includeUnsubscribe,\n externalFooterHtml,\n footerInjectionMode,\n ]);\n\n const handleIframeLoad = useCallback(() => {\n builderWindowRef.current = iframeRef.current?.contentWindow ?? null;\n readyRef.current = false;\n runtimeOriginRef.current = null;\n setStatusSafely('loading');\n\n if (builderWindowRef.current) {\n try {\n builderWindowRef.current.postMessage(buildEnvelope(latestInitRef.current), '*');\n } catch {\n // Ignore and wait for normal READY/message flow.\n }\n }\n\n if (handshakeTimerRef.current) {\n window.clearTimeout(handshakeTimerRef.current);\n }\n if (handshakeRetryIntervalRef.current) {\n window.clearInterval(handshakeRetryIntervalRef.current);\n }\n\n let attempts = 0;\n const maxAttempts = 12;\n handshakeRetryIntervalRef.current = window.setInterval(() => {\n if (readyRef.current) {\n if (handshakeRetryIntervalRef.current) {\n window.clearInterval(handshakeRetryIntervalRef.current);\n handshakeRetryIntervalRef.current = null;\n }\n return;\n }\n attempts += 1;\n try {\n builderWindowRef.current?.postMessage(buildEnvelope(latestInitRef.current), '*');\n } catch {\n // Keep retrying until timeout.\n }\n if (attempts >= maxAttempts) {\n if (handshakeRetryIntervalRef.current) {\n window.clearInterval(handshakeRetryIntervalRef.current);\n handshakeRetryIntervalRef.current = null;\n }\n }\n }, 1000);\n\n handshakeTimerRef.current = window.setTimeout(() => {\n if (readyRef.current) {\n return;\n }\n if (handshakeRetryIntervalRef.current) {\n window.clearInterval(handshakeRetryIntervalRef.current);\n handshakeRetryIntervalRef.current = null;\n }\n setStatusSafely('error');\n onAuthError?.('Builder handshake failed or authentication was rejected.');\n }, 12000);\n }, [onAuthError, setStatusSafely]);\n\n useImperativeHandle(\n ref,\n () => ({\n reload() {\n queueRef.current = [];\n readyRef.current = false;\n builderWindowRef.current = null;\n runtimeOriginRef.current = null;\n if (handshakeTimerRef.current) {\n window.clearTimeout(handshakeTimerRef.current);\n handshakeTimerRef.current = null;\n }\n if (handshakeRetryIntervalRef.current) {\n window.clearInterval(handshakeRetryIntervalRef.current);\n handshakeRetryIntervalRef.current = null;\n }\n setStatusSafely('loading');\n setReloadKey((key) => key + 1);\n },\n }),\n [setStatusSafely]\n );\n\n useEffect(() => {\n return () => {\n if (handshakeTimerRef.current) {\n window.clearTimeout(handshakeTimerRef.current);\n handshakeTimerRef.current = null;\n }\n if (handshakeRetryIntervalRef.current) {\n window.clearInterval(handshakeRetryIntervalRef.current);\n handshakeRetryIntervalRef.current = null;\n }\n };\n }, []);\n\n return (\n <div className={className} style={{ position: 'relative', width: '100%', height: '100%', ...style }}>\n <iframe\n key={reloadKey}\n ref={iframeRef}\n src={iframeSrc}\n title={iframeTitle}\n sandbox={sandbox}\n style={iframeStyle}\n loading=\"lazy\"\n allowFullScreen\n onLoad={handleIframeLoad}\n />\n {status !== 'ready' && (\n <div style={overlayStyle}>Connecting to builder…</div>\n )}\n </div>\n );\n}\n\nexport const EmailBuilder = forwardRef(EmailBuilderInner);\n","export const EMAIL_BUILDER_PROTOCOL_VERSION = '1.0.0';\n\nexport type MessageType =\n | 'INIT'\n | 'READY'\n | 'CHANGE'\n | 'LOADED'\n | 'SAVE'\n | 'UPLOAD'\n | 'UPLOAD_SUCCESS'\n | 'LIST_ASSETS'\n | 'ASSETS_LIST'\n | 'DELETE_ASSET'\n | 'DELETE_ASSET_SUCCESS'\n | 'AUTH_ERROR';\n\nexport interface MessageMeta {\n id: string;\n correlationId?: string;\n version: string;\n sentAt: number;\n}\n\nexport type BuilderConfig = Record<string, unknown> | undefined;\n\nexport interface InitPayload {\n /** Inline HTML to import (optional if templateId is set and builder fetches server-side). */\n html?: string;\n /** When set, embedded builder fetches HTML from API using embed token + this id. */\n templateId?: string;\n config?: BuilderConfig;\n /**\n * Optional: host-provided footer HTML to be rendered by the builder (SDK mode).\n * If set, the builder can avoid importing its own footer and render this exact footer instead.\n */\n externalFooterHtml?: string;\n /**\n * Footer source selection.\n * - `default`: builder uses its existing behavior.\n * - `sdk`: builder uses `externalFooterHtml` (and should avoid importing/auto-generating footer).\n */\n footerInjectionMode?: 'default' | 'sdk';\n /** Show \"Powered by Public Circles\" branding in footer. Controlled by host based on add-on purchase. */\n showFooter?: boolean;\n /** Include unsubscribe link in footer. Controlled by host toggle. */\n includeUnsubscribe?: boolean;\n}\n\nexport interface ChangePayload {\n html: string;\n}\n\nexport interface LoadedPayload {\n html: string;\n}\n\nexport interface SavePayload {\n html: string;\n}\n\nexport interface UploadPayload {\n file: File;\n}\n\nexport interface UploadSuccessPayload {\n url: string;\n}\n\nexport interface AssetItem {\n url: string;\n id?: string;\n name?: string;\n thumbnailUrl?: string;\n}\n\nexport interface ListAssetsPayload {\n limit?: number;\n}\n\nexport interface AssetsListPayload {\n assets: AssetItem[];\n}\n\nexport interface DeleteAssetPayload {\n id?: string;\n url?: string;\n}\n\nexport interface DeleteAssetSuccessPayload {\n success: boolean;\n}\n\nexport interface AuthErrorPayload {\n message: string;\n}\n\nexport type Message =\n | { type: 'INIT'; payload: InitPayload; meta?: MessageMeta }\n | { type: 'READY'; meta?: MessageMeta }\n | { type: 'CHANGE'; payload: ChangePayload; meta?: MessageMeta }\n | { type: 'LOADED'; payload: LoadedPayload; meta?: MessageMeta }\n | { type: 'SAVE'; payload: SavePayload; meta?: MessageMeta }\n | { type: 'UPLOAD'; payload: UploadPayload; meta?: MessageMeta }\n | { type: 'UPLOAD_SUCCESS'; payload: UploadSuccessPayload; meta?: MessageMeta }\n | { type: 'LIST_ASSETS'; payload?: ListAssetsPayload; meta?: MessageMeta }\n | { type: 'ASSETS_LIST'; payload: AssetsListPayload; meta?: MessageMeta }\n | { type: 'DELETE_ASSET'; payload: DeleteAssetPayload; meta?: MessageMeta }\n | { type: 'DELETE_ASSET_SUCCESS'; payload: DeleteAssetSuccessPayload; meta?: MessageMeta }\n | { type: 'AUTH_ERROR'; payload: AuthErrorPayload; meta?: MessageMeta };\n\nexport type BuilderToHostMessage = Extract<\n Message,\n { type: 'READY' | 'CHANGE' | 'LOADED' | 'SAVE' | 'UPLOAD' | 'LIST_ASSETS' | 'DELETE_ASSET' | 'AUTH_ERROR' }\n>;\n\nexport type HostToBuilderMessage = Extract<\n Message,\n { type: 'INIT' | 'UPLOAD_SUCCESS' | 'ASSETS_LIST' | 'DELETE_ASSET_SUCCESS' }\n>;\n\nconst VALID_TYPES: MessageType[] = [\n 'INIT',\n 'READY',\n 'CHANGE',\n 'LOADED',\n 'SAVE',\n 'UPLOAD',\n 'UPLOAD_SUCCESS',\n 'LIST_ASSETS',\n 'ASSETS_LIST',\n 'DELETE_ASSET',\n 'DELETE_ASSET_SUCCESS',\n 'AUTH_ERROR',\n];\n\nexport function isMessageLike(value: unknown): value is Message {\n if (typeof value !== 'object' || value === null) {\n return false;\n }\n\n const candidate = value as { type?: unknown; payload?: unknown; meta?: unknown };\n if (typeof candidate.type !== 'string' || !VALID_TYPES.includes(candidate.type as MessageType)) {\n return false;\n }\n\n if ('meta' in candidate && candidate.meta !== undefined) {\n const meta = candidate.meta as Partial<MessageMeta>;\n if (\n (meta.id && typeof meta.id !== 'string') ||\n (meta.correlationId && typeof meta.correlationId !== 'string') ||\n (meta.version && typeof meta.version !== 'string') ||\n (meta.sentAt && typeof meta.sentAt !== 'number')\n ) {\n return false;\n }\n }\n\n return true;\n}\n\nexport function createMessageMeta(correlationId?: string): MessageMeta {\n const id = typeof crypto !== 'undefined' && 'randomUUID' in crypto\n ? crypto.randomUUID()\n : Math.random().toString(36).slice(2);\n return {\n id,\n correlationId,\n version: EMAIL_BUILDER_PROTOCOL_VERSION,\n sentAt: Date.now(),\n };\n}\n","import type { BuilderToHostMessage, HostToBuilderMessage, MessageMeta } from './protocol';\nimport { createMessageMeta, isMessageLike } from './protocol';\n\nexport function deriveAllowedOrigin(src: string, override?: string): string {\n if (override) {\n const parsed = new URL(override);\n if (parsed.origin === 'null') {\n throw new Error('allowedOrigin must resolve to a valid origin');\n }\n return parsed.origin;\n }\n\n let parsed: URL;\n try {\n parsed = new URL(src);\n } catch {\n throw new Error('EmailBuilder `src` must be an absolute URL (e.g. https://example.com)');\n }\n if (!parsed.origin || parsed.origin === 'null') {\n throw new Error('EmailBuilder requires an absolute src URL with a valid origin');\n }\n return parsed.origin;\n}\n\nexport function buildEnvelope<T extends HostToBuilderMessage>(message: T, correlationId?: string): T {\n const meta: MessageMeta = createMessageMeta(correlationId);\n return { ...message, meta } as T;\n}\n\nexport function sanitizeIncomingMessage(\n event: MessageEvent,\n allowedOrigin: string,\n iframeWindow: Window | null\n): BuilderToHostMessage | null {\n if (event.origin !== allowedOrigin) {\n return null;\n }\n\n if (!iframeWindow || event.source !== iframeWindow) {\n return null;\n }\n\n if (!isMessageLike(event.data)) {\n return null;\n }\n\n return event.data as BuilderToHostMessage;\n}\n\nexport function stableSignature(value: unknown): string {\n return JSON.stringify(value ?? null);\n}\n"],"mappings":"mbAAA,IAAAA,GAAA,GAAAC,GAAAD,GAAA,oCAAAE,GAAA,iBAAAC,GAAA,sBAAAC,EAAA,kBAAAC,IAAA,eAAAC,GAAAN,ICAA,IAAAO,EAUO,iBCVA,IAAMC,GAAiC,QAwHxCC,GAA6B,CACjC,OACA,QACA,SACA,SACA,OACA,SACA,iBACA,cACA,cACA,eACA,uBACA,YACF,EAEO,SAASC,EAAcC,EAAkC,CAC9D,GAAI,OAAOA,GAAU,UAAYA,IAAU,KACzC,MAAO,GAGT,IAAMC,EAAYD,EAClB,GAAI,OAAOC,EAAU,MAAS,UAAY,CAACH,GAAY,SAASG,EAAU,IAAmB,EAC3F,MAAO,GAGT,GAAI,SAAUA,GAAaA,EAAU,OAAS,OAAW,CACvD,IAAMC,EAAOD,EAAU,KACvB,GACGC,EAAK,IAAM,OAAOA,EAAK,IAAO,UAC9BA,EAAK,eAAiB,OAAOA,EAAK,eAAkB,UACpDA,EAAK,SAAW,OAAOA,EAAK,SAAY,UACxCA,EAAK,QAAU,OAAOA,EAAK,QAAW,SAEvC,MAAO,EAEX,CAEA,MAAO,EACT,CAEO,SAASC,EAAkBC,EAAqC,CAIrE,MAAO,CACL,GAJS,OAAO,QAAW,aAAe,eAAgB,OACxD,OAAO,WAAW,EAClB,KAAK,OAAO,EAAE,SAAS,EAAE,EAAE,MAAM,CAAC,EAGpC,cAAAA,EACA,QAASP,GACT,OAAQ,KAAK,IAAI,CACnB,CACF,CCvKO,SAASQ,GAAoBC,EAAaC,EAA2B,CAC1E,GAAIA,EAAU,CACZ,IAAMC,EAAS,IAAI,IAAID,CAAQ,EAC/B,GAAIC,EAAO,SAAW,OACpB,MAAM,IAAI,MAAM,8CAA8C,EAEhE,OAAOA,EAAO,MAChB,CAEA,IAAIA,EACJ,GAAI,CACFA,EAAS,IAAI,IAAIF,CAAG,CACtB,MAAQ,CACN,MAAM,IAAI,MAAM,uEAAuE,CACzF,CACA,GAAI,CAACE,EAAO,QAAUA,EAAO,SAAW,OACtC,MAAM,IAAI,MAAM,+DAA+D,EAEjF,OAAOA,EAAO,MAChB,CAEO,SAASC,EAA8CC,EAAYC,EAA2B,CACnG,IAAMC,EAAoBC,EAAkBF,CAAa,EACzD,MAAO,CAAE,GAAGD,EAAS,KAAAE,CAAK,CAC5B,CAsBO,SAASE,EAAgBC,EAAwB,CACtD,OAAO,KAAK,UAAUA,GAAA,KAAAA,EAAS,IAAI,CACrC,CFqaI,IAAAC,EAAA,6BAxcEC,GAAkB,8CAClBC,GAAuB,iEACvBC,GAAsB,gDAOtBC,GAA8B,CAClC,SAAU,WACV,MAAO,EACP,QAAS,OACT,WAAY,SACZ,eAAgB,SAChB,SAAU,WACV,WAAY,IACZ,WAAY,iEACZ,MAAO,OACP,OAAQ,CACV,EAEMC,GAA6B,CACjC,OAAQ,OACR,MAAO,OACP,OAAQ,MACV,EAEA,SAASC,GAAyBC,EAAaC,EAA0B,CACvE,GAAI,CAACA,EACH,OAAOD,EAET,GAAI,CACF,IAAME,EAAI,IAAI,IAAIF,EAAK,OAAO,QAAW,YAAc,OAAO,SAAS,KAAO,qBAAqB,EACnG,OAAAE,EAAE,aAAa,IAAI,UAAW,MAAM,EACpCA,EAAE,aAAa,IAAI,cAAe,MAAM,EACjCA,EAAE,SAAS,CACpB,MAAQ,CACN,IAAMC,EAAMH,EAAI,SAAS,GAAG,EAAI,IAAM,IACtC,MAAO,GAAGA,CAAG,GAAGG,CAAG,+BACrB,CACF,CAEA,SAASC,GAAsBJ,EAAaK,EAAwC,CAClF,GAAI,CAACA,GAAc,CAACA,EAAW,KAAK,EAClC,OAAOL,EAET,GAAI,CACF,IAAME,EAAI,IAAI,IAAIF,EAAK,OAAO,QAAW,YAAc,OAAO,SAAS,KAAO,qBAAqB,EACnG,OAAAE,EAAE,aAAa,IAAI,aAAcG,CAAU,EACpCH,EAAE,SAAS,CACpB,MAAQ,CACN,IAAMC,EAAMH,EAAI,SAAS,GAAG,EAAI,IAAM,IACtC,MAAO,GAAGA,CAAG,GAAGG,CAAG,cAAc,mBAAmBE,CAAU,CAAC,EACjE,CACF,CAEA,SAASC,GACP,CACE,IAAAN,EACA,YAAAO,EACA,WAAAF,EACA,WAAAG,EACA,OAAAC,EACA,WAAAC,EACA,mBAAAC,EACA,mBAAAC,EACA,oBAAAC,EACA,QAAAZ,GAAU,GACV,YAAAa,GAAc,GACd,UAAAC,GACA,MAAAC,GACA,YAAAC,GAAc,gBACd,QAAAC,GAAUxB,GACV,SAAAyB,EACA,OAAAC,EACA,OAAAC,EACA,SAAAC,EACA,aAAAC,EACA,cAAAC,EACA,QAAAC,EACA,eAAAC,EACA,YAAAC,EACA,cAAAC,CACF,EACAC,GACA,CACA,IAAMC,GAAc9B,GAAA,YAAAA,EAAK,SAAUJ,GAC7BmC,EAAgB9B,IAAWa,GAC3BkB,KAAkB,WACtB,IAAOD,EAAgB,CAAE,GAAItB,GAAA,KAAAA,EAAU,CAAC,EAAI,QAAS,GAAM,YAAa,EAAK,EAAIA,EACjF,CAACA,EAAQsB,CAAa,CACxB,EACME,MAAY,WAChB,IAAM7B,GAAsBL,GAAyB+B,EAAaC,CAAa,EAAG1B,CAAU,EAC5F,CAACyB,EAAazB,EAAY0B,CAAa,CACzC,EAEMG,KAAY,UAA0B,IAAI,EAC1CC,KAAmB,UAAsB,IAAI,EAC7CC,KAAW,UAAO,EAAK,EACvBC,KAAY,UAA2B,SAAS,EAChD,CAACC,GAAQC,CAAS,KAAI,YAA6B,SAAS,EAC5D,CAACC,GAAWC,EAAY,KAAI,YAAS,CAAC,EACtCC,KAAW,UAAyB,CAAC,CAAC,EACtCC,KAAmB,UAAsB,IAAI,EAC7CC,KAAoB,UAAsB,IAAI,EAC9CC,KAA4B,UAAsB,IAAI,EAEtDC,EACJ,OAAOvC,GAAgB,SACnBA,EACAC,EACE,GACAb,GAEFoD,EACJlC,IACCD,GAAsBA,EAAmB,KAAK,EAAI,MAAQ,WAEvDoC,EAA2B,CAC/B,KAAMF,EACN,OAAQd,EACR,GAAIxB,EAAa,CAAE,WAAAA,CAAW,EAAI,CAAC,EACnC,WAAAE,EACA,mBAAAC,EACA,GAAIC,EAAqB,CAAE,mBAAAA,CAAmB,EAAI,CAAC,EACnD,oBAAqBmC,CACvB,EAEME,KAAmB,UAAOC,EAAgBF,CAAW,CAAC,EACtDG,KAAgB,UAA6B,CACjD,KAAM,OACN,QAASH,CACX,CAAC,EAEKI,MAAiB,WAAQ,IAAMC,GAAoBvB,EAAaF,CAAa,EAAG,CAACE,EAAaF,CAAa,CAAC,EAE5G0B,KAAsB,eAAY,IAAM,IAAK,CAAC,CAAC,EAE/CC,KAAkB,eACrBC,GAA6B,CACxBnB,EAAU,UAAYmB,IAG1BnB,EAAU,QAAUmB,EACpBjB,EAAUiB,CAAI,EACd9B,GAAA,MAAAA,EAAiB8B,GACnB,EACA,CAAC9B,CAAc,CACjB,EAEM+B,KAAc,eAClB,CAACC,EAA+BC,IAA2B,CAzK/D,IAAAC,EAAAC,EA0KM,IAAMC,GAASD,GAAAD,EAAA1B,EAAU,UAAV,YAAA0B,EAAmB,gBAAnB,KAAAC,EAAoC1B,EAAiB,QAKpE,GAJI2B,IACF3B,EAAiB,QAAU2B,GAGzB,CAAC3B,EAAiB,SAAW,CAACC,EAAS,QAAS,CAClDM,EAAS,QAAQ,KAAK,CAAE,QAAAgB,EAAS,cAAAC,CAAc,CAAC,EAChD,MACF,CAEAxB,EAAiB,QAAQ,YAAY4B,EAAcL,EAASC,CAAa,EAAGL,EAAoB,CAAC,CACnG,EACA,CAACA,CAAmB,CACtB,EAEMU,KAAa,eAAY,IAAM,CACnC,GAAI,CAAC5B,EAAS,SAAW,CAACD,EAAiB,QACzC,OAEF,IAAM8B,EAAU,CAAC,GAAGvB,EAAS,OAAO,EACpCA,EAAS,QAAU,CAAC,EACpBuB,EAAQ,QAAQ,CAAC,CAAE,QAAAP,EAAS,cAAAC,CAAc,IAAM,CA/LpD,IAAAC,GAgMMA,EAAAzB,EAAiB,UAAjB,MAAAyB,EAA0B,YACxBG,EAAcL,EAASC,CAAa,EACpCL,EAAoB,EAExB,CAAC,CACH,EAAG,CAACA,CAAmB,CAAC,EAElBY,KAAqB,eAAY,IAAM,CACvC9B,EAAS,UAGTQ,EAAkB,UACpB,OAAO,aAAaA,EAAkB,OAAO,EAC7CA,EAAkB,QAAU,MAE1BC,EAA0B,UAC5B,OAAO,cAAcA,EAA0B,OAAO,EACtDA,EAA0B,QAAU,MAEtCT,EAAS,QAAU,GACnBmB,EAAgB,OAAO,EACvB9B,GAAA,MAAAA,IACAgC,EAAYN,EAAc,OAAO,EACjCa,EAAW,EACb,EAAG,CAACA,EAAYvC,EAASgC,EAAaF,CAAe,CAAC,EAEhDY,MAAe,eACnB,MAAOC,GAAuC,CA3NlD,IAAAR,EAAAC,EA4NM,GAAIO,EAAa,OAAS,SAG1B,IAAI,CAAC9C,EAAU,CACb,QAAQ,KAAK,2EAA2E,EACxF,MACF,CACA,GAAI,CACF,IAAM+C,EAAM,MAAM/C,EAAS8C,EAAa,QAAQ,IAAI,EACpDX,EAAY,CAAE,KAAM,iBAAkB,QAAS,CAAE,IAAAY,CAAI,CAAE,GAAGT,EAAAQ,EAAa,OAAb,YAAAR,EAAmB,EAAE,CACjF,OAASU,EAAO,CACd,QAAQ,MAAM,0CAA2CA,CAAK,EAE9Db,EAAY,CAAE,KAAM,iBAAkB,QAAS,CAAE,IAAK,EAAG,CAAE,GAAGI,EAAAO,EAAa,OAAb,YAAAP,EAAmB,EAAE,CACrF,EACF,EACA,CAACvC,EAAUmC,CAAW,CACxB,EAEMc,MAAmB,eACvB,MAAOH,GAAuC,CAhPlD,IAAAR,EAAAC,EAAAW,EAiPM,GAAIJ,EAAa,OAAS,cAG1B,IAAI,CAAC7C,EAAc,CACjBkC,EAAY,CAAE,KAAM,cAAe,QAAS,CAAE,OAAQ,CAAC,CAAE,CAAE,GAAGG,EAAAQ,EAAa,OAAb,YAAAR,EAAmB,EAAE,EACnF,MACF,CACA,GAAI,CACF,IAAMa,EAAS,MAAMlD,EAAa6C,EAAa,OAAO,EACtDX,EACE,CAAE,KAAM,cAAe,QAAS,CAAE,OAAQ,MAAM,QAAQgB,CAAM,EAAIA,EAAS,CAAC,CAAE,CAAE,GAChFZ,EAAAO,EAAa,OAAb,YAAAP,EAAmB,EACrB,CACF,OAASS,EAAO,CACd,QAAQ,MAAM,+CAAgDA,CAAK,EACnEb,EAAY,CAAE,KAAM,cAAe,QAAS,CAAE,OAAQ,CAAC,CAAE,CAAE,GAAGe,EAAAJ,EAAa,OAAb,YAAAI,EAAmB,EAAE,CACrF,EACF,EACA,CAACjD,EAAckC,CAAW,CAC5B,EAEMiB,MAAoB,eACxB,MAAON,GAAuC,CAvQlD,IAAAR,EAAAC,EAAAW,EAwQM,GAAIJ,EAAa,OAAS,eAG1B,IAAI,CAAC5C,EAAe,CAClBiC,EAAY,CAAE,KAAM,uBAAwB,QAAS,CAAE,QAAS,EAAM,CAAE,GAAGG,EAAAQ,EAAa,OAAb,YAAAR,EAAmB,EAAE,EAChG,MACF,CACA,GAAI,CACF,IAAMe,EAAU,MAAMnD,EAAc4C,EAAa,SAAW,CAAC,CAAC,EAC9DX,EAAY,CAAE,KAAM,uBAAwB,QAAS,CAAE,QAAS,CAAC,CAACkB,CAAQ,CAAE,GAAGd,EAAAO,EAAa,OAAb,YAAAP,EAAmB,EAAE,CACtG,OAASS,EAAO,CACd,QAAQ,MAAM,gDAAiDA,CAAK,EACpEb,EAAY,CAAE,KAAM,uBAAwB,QAAS,CAAE,QAAS,EAAM,CAAE,GAAGe,EAAAJ,EAAa,OAAb,YAAAI,EAAmB,EAAE,CAClG,EACF,EACA,CAAChD,EAAeiC,CAAW,CAC7B,EAEMmB,KAAgB,eACnBC,GAAwB,CA3R7B,IAAAjB,EAAAC,EAAAW,GA4RM,IAAMM,GAAejB,GAAAD,EAAA1B,EAAU,UAAV,YAAA0B,EAAmB,gBAAnB,KAAAC,EAAoC1B,EAAiB,QAI1E,GAHI,CAAC2C,GAAgBD,EAAM,SAAWC,GAGlC,CAACC,EAAcF,EAAM,IAAI,EAC3B,OAEF,GAAI,CAAClC,EAAiB,QACpBA,EAAiB,QAAUkC,EAAM,eACxBA,EAAM,SAAWlC,EAAiB,QAC3C,OAEF,IAAMe,EAAUmB,EAAM,KAMtB,OAJIA,EAAM,QAAUA,EAAM,SAAW1C,EAAiB,UACpDA,EAAiB,QAAU0C,EAAM,QAG3BnB,EAAQ,KAAM,CACpB,IAAK,QACHQ,EAAmB,EACnB,MACF,IAAK,SACH/C,GAAA,MAAAA,EAAWuC,EAAQ,QAAQ,MAC3B,MACF,IAAK,SACHtC,GAAA,MAAAA,EAASsC,EAAQ,QAAQ,MACzB,MACF,IAAK,OACHrC,GAAA,MAAAA,EAASqC,EAAQ,QAAQ,MACzB,MACF,IAAK,SACES,GAAaT,CAAO,EACzB,MACF,IAAK,cACEa,GAAiBb,CAAO,EAC7B,MACF,IAAK,eACEgB,GAAkBhB,CAAO,EAC9B,MACF,IAAK,aAAc,CACjB,IAAMsB,KAAMR,GAAAd,EAAQ,UAAR,YAAAc,GAAiB,UAAW,sCACpCnC,EAAU,UAAY,UACxBA,EAAU,QAAU,QACpBE,EAAU,OAAO,EACjBZ,GAAA,MAAAA,EAAcqD,KAEhB,KACF,CACA,QACE,KACJ,CACF,EACA,CAACd,EAAoBC,GAAcI,GAAkBG,GAAmBvD,EAAUC,EAAQC,EAAQM,EAAa4B,CAAe,CAChI,KAEA,aAAU,IAAM,CACd,GAAI,OAAO,QAAW,YAGtB,cAAO,iBAAiB,UAAWqB,CAAa,EACzC,IAAM,CACX,OAAO,oBAAoB,UAAWA,CAAa,CACrD,CACF,EAAG,CAACA,CAAa,CAAC,KAElB,aAAU,IAAM,CACd,IAAMK,EAA2B,CAC/B,KAAMnC,EACN,OAAQd,EACR,GAAIxB,EAAa,CAAE,WAAAA,CAAW,EAAI,CAAC,EACnC,WAAAE,EACA,mBAAAC,EACA,GAAIC,IAAuB,OAAY,CAAE,mBAAAA,CAAmB,EAAI,CAAC,EACjE,oBAAqBmC,CACvB,EACMmC,EAAYhC,EAAgB+B,CAAW,EAC7C9B,EAAc,QAAU,CAAE,KAAM,OAAQ,QAAS8B,CAAY,EACzDC,IAAcjC,EAAiB,SAAWb,EAAS,SACrDqB,EAAYN,EAAc,OAAO,EAEnCF,EAAiB,QAAUiC,CAC7B,EAAG,CACDlD,EACAc,EACAW,EACAjD,EACAE,EACAC,EACAC,EACAC,CACF,CAAC,EAED,IAAMsE,MAAmB,eAAY,IAAM,CAzX7C,IAAAvB,EAAAC,EA+XI,GALA1B,EAAiB,SAAU0B,GAAAD,EAAA1B,EAAU,UAAV,YAAA0B,EAAmB,gBAAnB,KAAAC,EAAoC,KAC/DzB,EAAS,QAAU,GACnBO,EAAiB,QAAU,KAC3BY,EAAgB,SAAS,EAErBpB,EAAiB,QACnB,GAAI,CACFA,EAAiB,QAAQ,YAAY4B,EAAcZ,EAAc,OAAO,EAAG,GAAG,CAChF,MAAQ,CAER,CAGEP,EAAkB,SACpB,OAAO,aAAaA,EAAkB,OAAO,EAE3CC,EAA0B,SAC5B,OAAO,cAAcA,EAA0B,OAAO,EAGxD,IAAIuC,EAAW,EACTC,EAAc,GACpBxC,EAA0B,QAAU,OAAO,YAAY,IAAM,CAhZjE,IAAAe,EAiZM,GAAIxB,EAAS,QAAS,CAChBS,EAA0B,UAC5B,OAAO,cAAcA,EAA0B,OAAO,EACtDA,EAA0B,QAAU,MAEtC,MACF,CACAuC,GAAY,EACZ,GAAI,EACFxB,EAAAzB,EAAiB,UAAjB,MAAAyB,EAA0B,YAAYG,EAAcZ,EAAc,OAAO,EAAG,IAC9E,MAAQ,CAER,CACIiC,GAAYC,GACVxC,EAA0B,UAC5B,OAAO,cAAcA,EAA0B,OAAO,EACtDA,EAA0B,QAAU,KAG1C,EAAG,GAAI,EAEPD,EAAkB,QAAU,OAAO,WAAW,IAAM,CAC9CR,EAAS,UAGTS,EAA0B,UAC5B,OAAO,cAAcA,EAA0B,OAAO,EACtDA,EAA0B,QAAU,MAEtCU,EAAgB,OAAO,EACvB5B,GAAA,MAAAA,EAAc,4DAChB,EAAG,IAAK,CACV,EAAG,CAACA,EAAa4B,CAAe,CAAC,EAEjC,gCACE1B,GACA,KAAO,CACL,QAAS,CACPa,EAAS,QAAU,CAAC,EACpBN,EAAS,QAAU,GACnBD,EAAiB,QAAU,KAC3BQ,EAAiB,QAAU,KACvBC,EAAkB,UACpB,OAAO,aAAaA,EAAkB,OAAO,EAC7CA,EAAkB,QAAU,MAE1BC,EAA0B,UAC5B,OAAO,cAAcA,EAA0B,OAAO,EACtDA,EAA0B,QAAU,MAEtCU,EAAgB,SAAS,EACzBd,GAAc6C,GAAQA,EAAM,CAAC,CAC/B,CACF,GACA,CAAC/B,CAAe,CAClB,KAEA,aAAU,IACD,IAAM,CACPX,EAAkB,UACpB,OAAO,aAAaA,EAAkB,OAAO,EAC7CA,EAAkB,QAAU,MAE1BC,EAA0B,UAC5B,OAAO,cAAcA,EAA0B,OAAO,EACtDA,EAA0B,QAAU,KAExC,EACC,CAAC,CAAC,KAGH,QAAC,OAAI,UAAW9B,GAAW,MAAO,CAAE,SAAU,WAAY,MAAO,OAAQ,OAAQ,OAAQ,GAAGC,EAAM,EAChG,oBAAC,UAEC,IAAKkB,EACL,IAAKD,GACL,MAAOhB,GACP,QAASC,GACT,MAAOpB,GACP,QAAQ,OACR,gBAAe,GACf,OAAQqF,IARH3C,EASP,EACCF,KAAW,YACV,OAAC,OAAI,MAAOzC,GAAc,uCAAsB,GAEpD,CAEJ,CAEO,IAAM0F,MAAe,cAAWjF,EAAiB","names":["index_exports","__export","EMAIL_BUILDER_PROTOCOL_VERSION","EmailBuilder","createMessageMeta","isMessageLike","__toCommonJS","import_react","EMAIL_BUILDER_PROTOCOL_VERSION","VALID_TYPES","isMessageLike","value","candidate","meta","createMessageMeta","correlationId","deriveAllowedOrigin","src","override","parsed","buildEnvelope","message","correlationId","meta","createMessageMeta","stableSignature","value","import_jsx_runtime","DEFAULT_SANDBOX","DEFAULT_INITIAL_HTML","DEFAULT_BUILDER_SRC","overlayStyle","iframeStyle","appendPreviewParamsToSrc","src","preview","u","sep","appendEmbedTokenToSrc","embedToken","EmailBuilderInner","initialHtml","templateId","config","showFooter","includeUnsubscribe","externalFooterHtml","footerInjectionMode","previewOnly","className","style","iframeTitle","sandbox","onChange","onLoad","onSave","onUpload","onListAssets","onDeleteAsset","onReady","onStatusChange","onAuthError","allowedOrigin","ref","resolvedSrc","isPreviewMode","effectiveConfig","iframeSrc","iframeRef","builderWindowRef","readyRef","statusRef","status","setStatus","reloadKey","setReloadKey","queueRef","runtimeOriginRef","handshakeTimerRef","handshakeRetryIntervalRef","effectiveInitialHtml","effectiveFooterMode","initPayload","initSignatureRef","stableSignature","latestInitRef","expectedOrigin","deriveAllowedOrigin","resolveTargetOrigin","setStatusSafely","next","postMessage","message","correlationId","_a","_b","target","buildEnvelope","flushQueue","pending","handleReadyMessage","handleUpload","eventMessage","url","error","handleListAssets","_c","assets","handleDeleteAsset","success","handleMessage","event","iframeWindow","isMessageLike","msg","nextPayload","signature","handleIframeLoad","attempts","maxAttempts","key","EmailBuilder"]}
|
|
1
|
+
{"version":3,"sources":["../src/index.ts","../src/EmailBuilder.tsx","../src/protocol.ts","../src/utils.ts"],"sourcesContent":["export * from './EmailBuilder';\nexport * from './types';\nexport * from './protocol';\n","import React, {\n CSSProperties,\n ForwardedRef,\n forwardRef,\n useCallback,\n useEffect,\n useImperativeHandle,\n useMemo,\n useRef,\n useState,\n} from 'react';\nimport type { BuilderToHostMessage, HostToBuilderMessage, InitPayload } from './protocol';\nimport { isMessageLike } from './protocol';\nimport { buildEnvelope, deriveAllowedOrigin, stableSignature } from './utils';\nimport type { EmailBuilderHandle, EmailBuilderProps, EmailBuilderStatus } from './types';\n\nconst DEFAULT_SANDBOX = 'allow-scripts allow-same-origin allow-forms';\nconst DEFAULT_INITIAL_HTML = '<h1>Hello World</h1><p>Start building your email template.</p>';\nconst DEFAULT_BUILDER_SRC = 'https://pc-email-template-builder.netlify.app';\n\ntype PendingMessage = {\n message: HostToBuilderMessage;\n correlationId?: string;\n};\n\nconst overlayStyle: CSSProperties = {\n position: 'absolute',\n inset: 0,\n display: 'flex',\n alignItems: 'center',\n justifyContent: 'center',\n fontSize: '0.875rem',\n fontWeight: 500,\n background: 'linear-gradient(135deg, rgba(9,9,9,0.65), rgba(33,33,33,0.85))',\n color: '#fff',\n zIndex: 2,\n};\n\nconst iframeStyle: CSSProperties = {\n border: 'none',\n width: '100%',\n height: '100%',\n};\n\nfunction appendPreviewParamsToSrc(src: string, preview: boolean): string {\n if (!preview) {\n return src;\n }\n try {\n const u = new URL(src, typeof window !== 'undefined' ? window.location.href : 'https://example.com');\n u.searchParams.set('preview', 'true');\n u.searchParams.set('previewOnly', 'true');\n return u.toString();\n } catch {\n const sep = src.includes('?') ? '&' : '?';\n return `${src}${sep}preview=true&previewOnly=true`;\n }\n}\n\nfunction appendEmbedTokenToSrc(src: string, embedToken: string | undefined): string {\n if (!embedToken || !embedToken.trim()) {\n return src;\n }\n try {\n const u = new URL(src, typeof window !== 'undefined' ? window.location.href : 'https://example.com');\n u.searchParams.set('embedToken', embedToken);\n return u.toString();\n } catch {\n const sep = src.includes('?') ? '&' : '?';\n return `${src}${sep}embedToken=${encodeURIComponent(embedToken)}`;\n }\n}\n\nfunction EmailBuilderInner(\n {\n src,\n initialHtml,\n mergeTags,\n mergeTagTrigger,\n embedToken,\n templateId,\n config,\n showFooter,\n includeUnsubscribe,\n externalFooterHtml,\n footerInjectionMode,\n preview = false,\n previewOnly = false,\n className,\n style,\n hideLoadingOverlay = false,\n iframeTitle = 'Email Builder',\n sandbox = DEFAULT_SANDBOX,\n onChange,\n onLoad,\n onSave,\n onUpload,\n onListAssets,\n onDeleteAsset,\n onReady,\n onStatusChange,\n onAuthError,\n allowedOrigin,\n }: EmailBuilderProps,\n ref: ForwardedRef<EmailBuilderHandle>\n) {\n const resolvedSrc = src?.trim() || DEFAULT_BUILDER_SRC;\n const isPreviewMode = preview || previewOnly;\n const effectiveConfig = useMemo(\n () => (isPreviewMode ? { ...(config ?? {}), preview: true, previewOnly: true } : config),\n [config, isPreviewMode]\n );\n const iframeSrc = useMemo(\n () => appendEmbedTokenToSrc(appendPreviewParamsToSrc(resolvedSrc, isPreviewMode), embedToken),\n [resolvedSrc, embedToken, isPreviewMode]\n );\n\n const iframeRef = useRef<HTMLIFrameElement>(null);\n const builderWindowRef = useRef<Window | null>(null);\n const readyRef = useRef(false);\n const statusRef = useRef<EmailBuilderStatus>('loading');\n const [status, setStatus] = useState<EmailBuilderStatus>('loading');\n const [reloadKey, setReloadKey] = useState(0);\n const queueRef = useRef<PendingMessage[]>([]);\n const runtimeOriginRef = useRef<string | null>(null);\n const handshakeTimerRef = useRef<number | null>(null);\n const handshakeRetryIntervalRef = useRef<number | null>(null);\n\n const effectiveInitialHtml =\n typeof initialHtml === 'string'\n ? initialHtml\n : templateId\n ? ''\n : DEFAULT_INITIAL_HTML;\n\n const effectiveFooterMode =\n footerInjectionMode ||\n (externalFooterHtml && externalFooterHtml.trim() ? 'sdk' : 'default');\n\n const initPayload: InitPayload = {\n html: effectiveInitialHtml,\n ...(Array.isArray(mergeTags) ? { mergeTags } : {}),\n ...(typeof mergeTagTrigger === 'string' && mergeTagTrigger.trim()\n ? { mergeTagTrigger: mergeTagTrigger.trim() }\n : {}),\n config: effectiveConfig,\n ...(templateId ? { templateId } : {}),\n showFooter,\n includeUnsubscribe,\n ...(externalFooterHtml ? { externalFooterHtml } : {}),\n footerInjectionMode: effectiveFooterMode,\n };\n\n const initSignatureRef = useRef(stableSignature(initPayload));\n const latestInitRef = useRef<HostToBuilderMessage>({\n type: 'INIT',\n payload: initPayload,\n });\n\n const expectedOrigin = useMemo(() => deriveAllowedOrigin(resolvedSrc, allowedOrigin), [resolvedSrc, allowedOrigin]);\n\n const resolveTargetOrigin = useCallback(() => '*', []);\n\n const setStatusSafely = useCallback(\n (next: EmailBuilderStatus) => {\n if (statusRef.current === next) {\n return;\n }\n statusRef.current = next;\n setStatus(next);\n onStatusChange?.(next);\n },\n [onStatusChange]\n );\n\n const postMessage = useCallback(\n (message: HostToBuilderMessage, correlationId?: string) => {\n const target = iframeRef.current?.contentWindow ?? builderWindowRef.current;\n if (target) {\n builderWindowRef.current = target;\n }\n\n if (!builderWindowRef.current || !readyRef.current) {\n queueRef.current.push({ message, correlationId });\n return;\n }\n\n builderWindowRef.current.postMessage(buildEnvelope(message, correlationId), resolveTargetOrigin());\n },\n [resolveTargetOrigin]\n );\n\n const flushQueue = useCallback(() => {\n if (!readyRef.current || !builderWindowRef.current) {\n return;\n }\n const pending = [...queueRef.current];\n queueRef.current = [];\n pending.forEach(({ message, correlationId }) => {\n builderWindowRef.current?.postMessage(\n buildEnvelope(message, correlationId),\n resolveTargetOrigin()\n );\n });\n }, [resolveTargetOrigin]);\n\n const handleReadyMessage = useCallback(() => {\n if (readyRef.current) {\n return;\n }\n if (handshakeTimerRef.current) {\n window.clearTimeout(handshakeTimerRef.current);\n handshakeTimerRef.current = null;\n }\n if (handshakeRetryIntervalRef.current) {\n window.clearInterval(handshakeRetryIntervalRef.current);\n handshakeRetryIntervalRef.current = null;\n }\n readyRef.current = true;\n setStatusSafely('ready');\n onReady?.();\n postMessage(latestInitRef.current);\n flushQueue();\n }, [flushQueue, onReady, postMessage, setStatusSafely]);\n\n const handleUpload = useCallback(\n async (eventMessage: BuilderToHostMessage) => {\n if (eventMessage.type !== 'UPLOAD') {\n return;\n }\n if (!onUpload) {\n console.warn('[EmailBuilderSDK] Upload requested but no onUpload handler is configured.');\n return;\n }\n try {\n const url = await onUpload(eventMessage.payload.file);\n postMessage({ type: 'UPLOAD_SUCCESS', payload: { url } }, eventMessage.meta?.id);\n } catch (error) {\n console.error('[EmailBuilderSDK] Upload handler failed', error);\n // Keep builder session ready; upload failure should not flip global iframe status.\n postMessage({ type: 'UPLOAD_SUCCESS', payload: { url: '' } }, eventMessage.meta?.id);\n }\n },\n [onUpload, postMessage]\n );\n\n const handleListAssets = useCallback(\n async (eventMessage: BuilderToHostMessage) => {\n if (eventMessage.type !== 'LIST_ASSETS') {\n return;\n }\n if (!onListAssets) {\n postMessage({ type: 'ASSETS_LIST', payload: { assets: [] } }, eventMessage.meta?.id);\n return;\n }\n try {\n const assets = await onListAssets(eventMessage.payload);\n postMessage(\n { type: 'ASSETS_LIST', payload: { assets: Array.isArray(assets) ? assets : [] } },\n eventMessage.meta?.id\n );\n } catch (error) {\n console.error('[EmailBuilderSDK] list assets handler failed', error);\n postMessage({ type: 'ASSETS_LIST', payload: { assets: [] } }, eventMessage.meta?.id);\n }\n },\n [onListAssets, postMessage]\n );\n\n const handleDeleteAsset = useCallback(\n async (eventMessage: BuilderToHostMessage) => {\n if (eventMessage.type !== 'DELETE_ASSET') {\n return;\n }\n if (!onDeleteAsset) {\n postMessage({ type: 'DELETE_ASSET_SUCCESS', payload: { success: false } }, eventMessage.meta?.id);\n return;\n }\n try {\n const success = await onDeleteAsset(eventMessage.payload || {});\n postMessage({ type: 'DELETE_ASSET_SUCCESS', payload: { success: !!success } }, eventMessage.meta?.id);\n } catch (error) {\n console.error('[EmailBuilderSDK] delete asset handler failed', error);\n postMessage({ type: 'DELETE_ASSET_SUCCESS', payload: { success: false } }, eventMessage.meta?.id);\n }\n },\n [onDeleteAsset, postMessage]\n );\n\n const handleMessage = useCallback(\n (event: MessageEvent) => {\n const iframeWindow = iframeRef.current?.contentWindow ?? builderWindowRef.current;\n if (!iframeWindow || event.source !== iframeWindow) {\n return;\n }\n if (!isMessageLike(event.data)) {\n return;\n }\n if (!runtimeOriginRef.current) {\n runtimeOriginRef.current = event.origin;\n } else if (event.origin !== runtimeOriginRef.current) {\n return;\n }\n const message = event.data as BuilderToHostMessage;\n\n if (event.source && event.source !== builderWindowRef.current) {\n builderWindowRef.current = event.source as Window;\n }\n\n switch (message.type) {\n case 'READY':\n handleReadyMessage();\n break;\n case 'CHANGE':\n onChange?.(message.payload.html);\n break;\n case 'LOADED':\n onLoad?.(message.payload.html);\n break;\n case 'SAVE':\n onSave?.(message.payload.html);\n break;\n case 'UPLOAD':\n void handleUpload(message);\n break;\n case 'LIST_ASSETS':\n void handleListAssets(message);\n break;\n case 'DELETE_ASSET':\n void handleDeleteAsset(message);\n break;\n case 'AUTH_ERROR': {\n const msg = message.payload?.message || 'Email builder authentication failed';\n if (statusRef.current !== 'error') {\n statusRef.current = 'error';\n setStatus('error');\n onAuthError?.(msg);\n }\n break;\n }\n default:\n break;\n }\n },\n [handleReadyMessage, handleUpload, handleListAssets, handleDeleteAsset, onChange, onLoad, onSave, onAuthError, setStatusSafely]\n );\n\n useEffect(() => {\n if (typeof window === 'undefined') {\n return;\n }\n window.addEventListener('message', handleMessage);\n return () => {\n window.removeEventListener('message', handleMessage);\n };\n }, [handleMessage]);\n\n useEffect(() => {\n const nextPayload: InitPayload = {\n html: effectiveInitialHtml,\n ...(Array.isArray(mergeTags) ? { mergeTags } : {}),\n ...(typeof mergeTagTrigger === 'string' && mergeTagTrigger.trim()\n ? { mergeTagTrigger: mergeTagTrigger.trim() }\n : {}),\n config: effectiveConfig,\n ...(templateId ? { templateId } : {}),\n showFooter,\n includeUnsubscribe,\n ...(externalFooterHtml !== undefined ? { externalFooterHtml } : {}),\n footerInjectionMode: effectiveFooterMode,\n };\n const signature = stableSignature(nextPayload);\n latestInitRef.current = { type: 'INIT', payload: nextPayload };\n if (signature !== initSignatureRef.current && readyRef.current) {\n postMessage(latestInitRef.current);\n }\n initSignatureRef.current = signature;\n }, [\n effectiveConfig,\n effectiveInitialHtml,\n mergeTags,\n mergeTagTrigger,\n postMessage,\n templateId,\n showFooter,\n includeUnsubscribe,\n externalFooterHtml,\n footerInjectionMode,\n ]);\n\n const handleIframeLoad = useCallback(() => {\n builderWindowRef.current = iframeRef.current?.contentWindow ?? null;\n readyRef.current = false;\n runtimeOriginRef.current = null;\n setStatusSafely('loading');\n\n if (builderWindowRef.current) {\n try {\n builderWindowRef.current.postMessage(buildEnvelope(latestInitRef.current), '*');\n } catch {\n // Ignore and wait for normal READY/message flow.\n }\n }\n\n if (handshakeTimerRef.current) {\n window.clearTimeout(handshakeTimerRef.current);\n }\n if (handshakeRetryIntervalRef.current) {\n window.clearInterval(handshakeRetryIntervalRef.current);\n }\n\n let attempts = 0;\n const maxAttempts = 12;\n handshakeRetryIntervalRef.current = window.setInterval(() => {\n if (readyRef.current) {\n if (handshakeRetryIntervalRef.current) {\n window.clearInterval(handshakeRetryIntervalRef.current);\n handshakeRetryIntervalRef.current = null;\n }\n return;\n }\n attempts += 1;\n try {\n builderWindowRef.current?.postMessage(buildEnvelope(latestInitRef.current), '*');\n } catch {\n // Keep retrying until timeout.\n }\n if (attempts >= maxAttempts) {\n if (handshakeRetryIntervalRef.current) {\n window.clearInterval(handshakeRetryIntervalRef.current);\n handshakeRetryIntervalRef.current = null;\n }\n }\n }, 1000);\n\n handshakeTimerRef.current = window.setTimeout(() => {\n if (readyRef.current) {\n return;\n }\n if (handshakeRetryIntervalRef.current) {\n window.clearInterval(handshakeRetryIntervalRef.current);\n handshakeRetryIntervalRef.current = null;\n }\n setStatusSafely('error');\n onAuthError?.('Builder handshake failed or authentication was rejected.');\n }, 12000);\n }, [onAuthError, setStatusSafely]);\n\n useImperativeHandle(\n ref,\n () => ({\n reload() {\n queueRef.current = [];\n readyRef.current = false;\n builderWindowRef.current = null;\n runtimeOriginRef.current = null;\n if (handshakeTimerRef.current) {\n window.clearTimeout(handshakeTimerRef.current);\n handshakeTimerRef.current = null;\n }\n if (handshakeRetryIntervalRef.current) {\n window.clearInterval(handshakeRetryIntervalRef.current);\n handshakeRetryIntervalRef.current = null;\n }\n setStatusSafely('loading');\n setReloadKey((key) => key + 1);\n },\n }),\n [setStatusSafely]\n );\n\n useEffect(() => {\n return () => {\n if (handshakeTimerRef.current) {\n window.clearTimeout(handshakeTimerRef.current);\n handshakeTimerRef.current = null;\n }\n if (handshakeRetryIntervalRef.current) {\n window.clearInterval(handshakeRetryIntervalRef.current);\n handshakeRetryIntervalRef.current = null;\n }\n };\n }, []);\n\n return (\n <div className={className} style={{ position: 'relative', width: '100%', height: '100%', ...style }}>\n <iframe\n key={reloadKey}\n ref={iframeRef}\n src={iframeSrc}\n title={iframeTitle}\n sandbox={sandbox}\n style={iframeStyle}\n loading=\"lazy\"\n allowFullScreen\n onLoad={handleIframeLoad}\n />\n {!hideLoadingOverlay && status !== 'ready' && (\n <div style={overlayStyle}>Connecting to builder…</div>\n )}\n </div>\n );\n}\n\nexport const EmailBuilder = forwardRef(EmailBuilderInner);\n","export const EMAIL_BUILDER_PROTOCOL_VERSION = '1.0.0';\n\nexport type MessageType =\n | 'INIT'\n | 'READY'\n | 'CHANGE'\n | 'LOADED'\n | 'SAVE'\n | 'UPLOAD'\n | 'UPLOAD_SUCCESS'\n | 'LIST_ASSETS'\n | 'ASSETS_LIST'\n | 'DELETE_ASSET'\n | 'DELETE_ASSET_SUCCESS'\n | 'AUTH_ERROR';\n\nexport interface MessageMeta {\n id: string;\n correlationId?: string;\n version: string;\n sentAt: number;\n}\n\nexport type BuilderConfig = Record<string, unknown> | undefined;\n\nexport interface InitPayload {\n /** Inline HTML to import (optional if templateId is set and builder fetches server-side). */\n html?: string;\n /** Merge tag options used by the embedded editor dropdown. */\n mergeTags?: Array<{ label: string; value: string }>;\n /** Trigger used to open merge tag suggestions (defaults to '@'). */\n mergeTagTrigger?: string;\n /** When set, embedded builder fetches HTML from API using embed token + this id. */\n templateId?: string;\n config?: BuilderConfig;\n /**\n * Optional: host-provided footer HTML to be rendered by the builder (SDK mode).\n * If set, the builder can avoid importing its own footer and render this exact footer instead.\n */\n externalFooterHtml?: string;\n /**\n * Footer source selection.\n * - `default`: builder uses its existing behavior.\n * - `sdk`: builder uses `externalFooterHtml` (and should avoid importing/auto-generating footer).\n */\n footerInjectionMode?: 'default' | 'sdk';\n /** Show \"Powered by Public Circles\" branding in footer. Controlled by host based on add-on purchase. */\n showFooter?: boolean;\n /** Include unsubscribe link in footer. Controlled by host toggle. */\n includeUnsubscribe?: boolean;\n}\n\nexport interface ChangePayload {\n html: string;\n}\n\nexport interface LoadedPayload {\n html: string;\n}\n\nexport interface SavePayload {\n html: string;\n}\n\nexport interface UploadPayload {\n file: File;\n}\n\nexport interface UploadSuccessPayload {\n url: string;\n}\n\nexport interface AssetItem {\n url: string;\n id?: string;\n name?: string;\n thumbnailUrl?: string;\n}\n\nexport interface ListAssetsPayload {\n limit?: number;\n}\n\nexport interface AssetsListPayload {\n assets: AssetItem[];\n}\n\nexport interface DeleteAssetPayload {\n id?: string;\n url?: string;\n}\n\nexport interface DeleteAssetSuccessPayload {\n success: boolean;\n}\n\nexport interface AuthErrorPayload {\n message: string;\n}\n\nexport type Message =\n | { type: 'INIT'; payload: InitPayload; meta?: MessageMeta }\n | { type: 'READY'; meta?: MessageMeta }\n | { type: 'CHANGE'; payload: ChangePayload; meta?: MessageMeta }\n | { type: 'LOADED'; payload: LoadedPayload; meta?: MessageMeta }\n | { type: 'SAVE'; payload: SavePayload; meta?: MessageMeta }\n | { type: 'UPLOAD'; payload: UploadPayload; meta?: MessageMeta }\n | { type: 'UPLOAD_SUCCESS'; payload: UploadSuccessPayload; meta?: MessageMeta }\n | { type: 'LIST_ASSETS'; payload?: ListAssetsPayload; meta?: MessageMeta }\n | { type: 'ASSETS_LIST'; payload: AssetsListPayload; meta?: MessageMeta }\n | { type: 'DELETE_ASSET'; payload: DeleteAssetPayload; meta?: MessageMeta }\n | { type: 'DELETE_ASSET_SUCCESS'; payload: DeleteAssetSuccessPayload; meta?: MessageMeta }\n | { type: 'AUTH_ERROR'; payload: AuthErrorPayload; meta?: MessageMeta };\n\nexport type BuilderToHostMessage = Extract<\n Message,\n { type: 'READY' | 'CHANGE' | 'LOADED' | 'SAVE' | 'UPLOAD' | 'LIST_ASSETS' | 'DELETE_ASSET' | 'AUTH_ERROR' }\n>;\n\nexport type HostToBuilderMessage = Extract<\n Message,\n { type: 'INIT' | 'UPLOAD_SUCCESS' | 'ASSETS_LIST' | 'DELETE_ASSET_SUCCESS' }\n>;\n\nconst VALID_TYPES: MessageType[] = [\n 'INIT',\n 'READY',\n 'CHANGE',\n 'LOADED',\n 'SAVE',\n 'UPLOAD',\n 'UPLOAD_SUCCESS',\n 'LIST_ASSETS',\n 'ASSETS_LIST',\n 'DELETE_ASSET',\n 'DELETE_ASSET_SUCCESS',\n 'AUTH_ERROR',\n];\n\nexport function isMessageLike(value: unknown): value is Message {\n if (typeof value !== 'object' || value === null) {\n return false;\n }\n\n const candidate = value as { type?: unknown; payload?: unknown; meta?: unknown };\n if (typeof candidate.type !== 'string' || !VALID_TYPES.includes(candidate.type as MessageType)) {\n return false;\n }\n\n if ('meta' in candidate && candidate.meta !== undefined) {\n const meta = candidate.meta as Partial<MessageMeta>;\n if (\n (meta.id && typeof meta.id !== 'string') ||\n (meta.correlationId && typeof meta.correlationId !== 'string') ||\n (meta.version && typeof meta.version !== 'string') ||\n (meta.sentAt && typeof meta.sentAt !== 'number')\n ) {\n return false;\n }\n }\n\n return true;\n}\n\nexport function createMessageMeta(correlationId?: string): MessageMeta {\n const id = typeof crypto !== 'undefined' && 'randomUUID' in crypto\n ? crypto.randomUUID()\n : Math.random().toString(36).slice(2);\n return {\n id,\n correlationId,\n version: EMAIL_BUILDER_PROTOCOL_VERSION,\n sentAt: Date.now(),\n };\n}\n","import type { BuilderToHostMessage, HostToBuilderMessage, MessageMeta } from './protocol';\nimport { createMessageMeta, isMessageLike } from './protocol';\n\nexport function deriveAllowedOrigin(src: string, override?: string): string {\n if (override) {\n const parsed = new URL(override);\n if (parsed.origin === 'null') {\n throw new Error('allowedOrigin must resolve to a valid origin');\n }\n return parsed.origin;\n }\n\n let parsed: URL;\n try {\n parsed = new URL(src);\n } catch {\n throw new Error('EmailBuilder `src` must be an absolute URL (e.g. https://example.com)');\n }\n if (!parsed.origin || parsed.origin === 'null') {\n throw new Error('EmailBuilder requires an absolute src URL with a valid origin');\n }\n return parsed.origin;\n}\n\nexport function buildEnvelope<T extends HostToBuilderMessage>(message: T, correlationId?: string): T {\n const meta: MessageMeta = createMessageMeta(correlationId);\n return { ...message, meta } as T;\n}\n\nexport function sanitizeIncomingMessage(\n event: MessageEvent,\n allowedOrigin: string,\n iframeWindow: Window | null\n): BuilderToHostMessage | null {\n if (event.origin !== allowedOrigin) {\n return null;\n }\n\n if (!iframeWindow || event.source !== iframeWindow) {\n return null;\n }\n\n if (!isMessageLike(event.data)) {\n return null;\n }\n\n return event.data as BuilderToHostMessage;\n}\n\nexport function stableSignature(value: unknown): string {\n return JSON.stringify(value ?? null);\n}\n"],"mappings":"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","names":["index_exports","__export","EMAIL_BUILDER_PROTOCOL_VERSION","EmailBuilder","createMessageMeta","isMessageLike","__toCommonJS","import_react","EMAIL_BUILDER_PROTOCOL_VERSION","VALID_TYPES","isMessageLike","value","candidate","meta","createMessageMeta","correlationId","deriveAllowedOrigin","src","override","parsed","buildEnvelope","message","correlationId","meta","createMessageMeta","stableSignature","value","import_jsx_runtime","DEFAULT_SANDBOX","DEFAULT_INITIAL_HTML","DEFAULT_BUILDER_SRC","overlayStyle","iframeStyle","appendPreviewParamsToSrc","src","preview","u","sep","appendEmbedTokenToSrc","embedToken","EmailBuilderInner","initialHtml","mergeTags","mergeTagTrigger","templateId","config","showFooter","includeUnsubscribe","externalFooterHtml","footerInjectionMode","previewOnly","className","style","hideLoadingOverlay","iframeTitle","sandbox","onChange","onLoad","onSave","onUpload","onListAssets","onDeleteAsset","onReady","onStatusChange","onAuthError","allowedOrigin","ref","resolvedSrc","isPreviewMode","effectiveConfig","iframeSrc","iframeRef","builderWindowRef","readyRef","statusRef","status","setStatus","reloadKey","setReloadKey","queueRef","runtimeOriginRef","handshakeTimerRef","handshakeRetryIntervalRef","effectiveInitialHtml","effectiveFooterMode","initPayload","initSignatureRef","stableSignature","latestInitRef","expectedOrigin","deriveAllowedOrigin","resolveTargetOrigin","setStatusSafely","next","postMessage","message","correlationId","_a","_b","target","buildEnvelope","flushQueue","pending","handleReadyMessage","handleUpload","eventMessage","url","error","handleListAssets","_c","assets","handleDeleteAsset","success","handleMessage","event","iframeWindow","isMessageLike","msg","nextPayload","signature","handleIframeLoad","attempts","maxAttempts","key","EmailBuilder"]}
|
package/dist/index.d.cts
CHANGED
|
@@ -12,6 +12,13 @@ type BuilderConfig = Record<string, unknown> | undefined;
|
|
|
12
12
|
interface InitPayload {
|
|
13
13
|
/** Inline HTML to import (optional if templateId is set and builder fetches server-side). */
|
|
14
14
|
html?: string;
|
|
15
|
+
/** Merge tag options used by the embedded editor dropdown. */
|
|
16
|
+
mergeTags?: Array<{
|
|
17
|
+
label: string;
|
|
18
|
+
value: string;
|
|
19
|
+
}>;
|
|
20
|
+
/** Trigger used to open merge tag suggestions (defaults to '@'). */
|
|
21
|
+
mergeTagTrigger?: string;
|
|
15
22
|
/** When set, embedded builder fetches HTML from API using embed token + this id. */
|
|
16
23
|
templateId?: string;
|
|
17
24
|
config?: BuilderConfig;
|
|
@@ -129,12 +136,20 @@ type UploadHandler = (file: File) => Promise<string>;
|
|
|
129
136
|
type ListAssetsHandler = (payload?: ListAssetsPayload) => Promise<AssetItem[]>;
|
|
130
137
|
type DeleteAssetHandler = (payload: DeleteAssetPayload) => Promise<boolean>;
|
|
131
138
|
type EmailBuilderStatus = 'idle' | 'loading' | 'ready' | 'error';
|
|
139
|
+
interface MergeTagOption {
|
|
140
|
+
label: string;
|
|
141
|
+
value: string;
|
|
142
|
+
}
|
|
132
143
|
interface EmailBuilderProps {
|
|
133
144
|
/**
|
|
134
145
|
* Optional builder app URL. If omitted, SDK uses the managed Circles builder endpoint.
|
|
135
146
|
*/
|
|
136
147
|
src?: string;
|
|
137
148
|
initialHtml?: string;
|
|
149
|
+
/** Merge tag options used by the embedded editor dropdown. */
|
|
150
|
+
mergeTags?: MergeTagOption[];
|
|
151
|
+
/** Trigger used to open merge tag suggestions (defaults to '@'). */
|
|
152
|
+
mergeTagTrigger?: string;
|
|
138
153
|
/**
|
|
139
154
|
* When set, the iframe URL includes `embedToken` so the builder can call your API.
|
|
140
155
|
* Create the token with POST /access-tokens (Bearer user JWT); the response string is the key.
|
|
@@ -167,6 +182,8 @@ interface EmailBuilderProps {
|
|
|
167
182
|
previewOnly?: boolean;
|
|
168
183
|
className?: string;
|
|
169
184
|
style?: CSSProperties;
|
|
185
|
+
/** Hide the SDK's default iframe loading overlay so the host can render its own loader. */
|
|
186
|
+
hideLoadingOverlay?: boolean;
|
|
170
187
|
iframeTitle?: string;
|
|
171
188
|
sandbox?: string;
|
|
172
189
|
onChange?: (html: string) => void;
|
|
@@ -192,4 +209,4 @@ interface EmailBuilderHandle {
|
|
|
192
209
|
|
|
193
210
|
declare const EmailBuilder: React.ForwardRefExoticComponent<EmailBuilderProps & React.RefAttributes<EmailBuilderHandle>>;
|
|
194
211
|
|
|
195
|
-
export { type AssetItem, type AssetsListPayload, type AuthErrorPayload, type BuilderConfig, type BuilderToHostMessage, type ChangePayload, type DeleteAssetHandler, type DeleteAssetPayload, type DeleteAssetSuccessPayload, EMAIL_BUILDER_PROTOCOL_VERSION, EmailBuilder, type EmailBuilderHandle, type EmailBuilderProps, type EmailBuilderStatus, type HostToBuilderMessage, type InitPayload, type ListAssetsHandler, type ListAssetsPayload, type LoadedPayload, type Message, type MessageMeta, type MessageType, type SavePayload, type UploadHandler, type UploadPayload, type UploadSuccessPayload, createMessageMeta, isMessageLike };
|
|
212
|
+
export { type AssetItem, type AssetsListPayload, type AuthErrorPayload, type BuilderConfig, type BuilderToHostMessage, type ChangePayload, type DeleteAssetHandler, type DeleteAssetPayload, type DeleteAssetSuccessPayload, EMAIL_BUILDER_PROTOCOL_VERSION, EmailBuilder, type EmailBuilderHandle, type EmailBuilderProps, type EmailBuilderStatus, type HostToBuilderMessage, type InitPayload, type ListAssetsHandler, type ListAssetsPayload, type LoadedPayload, type MergeTagOption, type Message, type MessageMeta, type MessageType, type SavePayload, type UploadHandler, type UploadPayload, type UploadSuccessPayload, createMessageMeta, isMessageLike };
|
package/dist/index.d.ts
CHANGED
|
@@ -12,6 +12,13 @@ type BuilderConfig = Record<string, unknown> | undefined;
|
|
|
12
12
|
interface InitPayload {
|
|
13
13
|
/** Inline HTML to import (optional if templateId is set and builder fetches server-side). */
|
|
14
14
|
html?: string;
|
|
15
|
+
/** Merge tag options used by the embedded editor dropdown. */
|
|
16
|
+
mergeTags?: Array<{
|
|
17
|
+
label: string;
|
|
18
|
+
value: string;
|
|
19
|
+
}>;
|
|
20
|
+
/** Trigger used to open merge tag suggestions (defaults to '@'). */
|
|
21
|
+
mergeTagTrigger?: string;
|
|
15
22
|
/** When set, embedded builder fetches HTML from API using embed token + this id. */
|
|
16
23
|
templateId?: string;
|
|
17
24
|
config?: BuilderConfig;
|
|
@@ -129,12 +136,20 @@ type UploadHandler = (file: File) => Promise<string>;
|
|
|
129
136
|
type ListAssetsHandler = (payload?: ListAssetsPayload) => Promise<AssetItem[]>;
|
|
130
137
|
type DeleteAssetHandler = (payload: DeleteAssetPayload) => Promise<boolean>;
|
|
131
138
|
type EmailBuilderStatus = 'idle' | 'loading' | 'ready' | 'error';
|
|
139
|
+
interface MergeTagOption {
|
|
140
|
+
label: string;
|
|
141
|
+
value: string;
|
|
142
|
+
}
|
|
132
143
|
interface EmailBuilderProps {
|
|
133
144
|
/**
|
|
134
145
|
* Optional builder app URL. If omitted, SDK uses the managed Circles builder endpoint.
|
|
135
146
|
*/
|
|
136
147
|
src?: string;
|
|
137
148
|
initialHtml?: string;
|
|
149
|
+
/** Merge tag options used by the embedded editor dropdown. */
|
|
150
|
+
mergeTags?: MergeTagOption[];
|
|
151
|
+
/** Trigger used to open merge tag suggestions (defaults to '@'). */
|
|
152
|
+
mergeTagTrigger?: string;
|
|
138
153
|
/**
|
|
139
154
|
* When set, the iframe URL includes `embedToken` so the builder can call your API.
|
|
140
155
|
* Create the token with POST /access-tokens (Bearer user JWT); the response string is the key.
|
|
@@ -167,6 +182,8 @@ interface EmailBuilderProps {
|
|
|
167
182
|
previewOnly?: boolean;
|
|
168
183
|
className?: string;
|
|
169
184
|
style?: CSSProperties;
|
|
185
|
+
/** Hide the SDK's default iframe loading overlay so the host can render its own loader. */
|
|
186
|
+
hideLoadingOverlay?: boolean;
|
|
170
187
|
iframeTitle?: string;
|
|
171
188
|
sandbox?: string;
|
|
172
189
|
onChange?: (html: string) => void;
|
|
@@ -192,4 +209,4 @@ interface EmailBuilderHandle {
|
|
|
192
209
|
|
|
193
210
|
declare const EmailBuilder: React.ForwardRefExoticComponent<EmailBuilderProps & React.RefAttributes<EmailBuilderHandle>>;
|
|
194
211
|
|
|
195
|
-
export { type AssetItem, type AssetsListPayload, type AuthErrorPayload, type BuilderConfig, type BuilderToHostMessage, type ChangePayload, type DeleteAssetHandler, type DeleteAssetPayload, type DeleteAssetSuccessPayload, EMAIL_BUILDER_PROTOCOL_VERSION, EmailBuilder, type EmailBuilderHandle, type EmailBuilderProps, type EmailBuilderStatus, type HostToBuilderMessage, type InitPayload, type ListAssetsHandler, type ListAssetsPayload, type LoadedPayload, type Message, type MessageMeta, type MessageType, type SavePayload, type UploadHandler, type UploadPayload, type UploadSuccessPayload, createMessageMeta, isMessageLike };
|
|
212
|
+
export { type AssetItem, type AssetsListPayload, type AuthErrorPayload, type BuilderConfig, type BuilderToHostMessage, type ChangePayload, type DeleteAssetHandler, type DeleteAssetPayload, type DeleteAssetSuccessPayload, EMAIL_BUILDER_PROTOCOL_VERSION, EmailBuilder, type EmailBuilderHandle, type EmailBuilderProps, type EmailBuilderStatus, type HostToBuilderMessage, type InitPayload, type ListAssetsHandler, type ListAssetsPayload, type LoadedPayload, type MergeTagOption, type Message, type MessageMeta, type MessageType, type SavePayload, type UploadHandler, type UploadPayload, type UploadSuccessPayload, createMessageMeta, isMessageLike };
|
package/dist/index.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import{forwardRef as
|
|
1
|
+
import{forwardRef as Pe,useCallback as f,useEffect as $,useImperativeHandle as Ue,useMemo as j,useRef as S,useState as le}from"react";var Ie="1.0.0",De=["INIT","READY","CHANGE","LOADED","SAVE","UPLOAD","UPLOAD_SUCCESS","LIST_ASSETS","ASSETS_LIST","DELETE_ASSET","DELETE_ASSET_SUCCESS","AUTH_ERROR"];function Y(r){if(typeof r!="object"||r===null)return!1;let n=r;if(typeof n.type!="string"||!De.includes(n.type))return!1;if("meta"in n&&n.meta!==void 0){let t=n.meta;if(t.id&&typeof t.id!="string"||t.correlationId&&typeof t.correlationId!="string"||t.version&&typeof t.version!="string"||t.sentAt&&typeof t.sentAt!="number")return!1}return!0}function ie(r){return{id:typeof crypto!="undefined"&&"randomUUID"in crypto?crypto.randomUUID():Math.random().toString(36).slice(2),correlationId:r,version:Ie,sentAt:Date.now()}}function oe(r,n){if(n){let p=new URL(n);if(p.origin==="null")throw new Error("allowedOrigin must resolve to a valid origin");return p.origin}let t;try{t=new URL(r)}catch{throw new Error("EmailBuilder `src` must be an absolute URL (e.g. https://example.com)")}if(!t.origin||t.origin==="null")throw new Error("EmailBuilder requires an absolute src URL with a valid origin");return t.origin}function L(r,n){let t=ie(n);return{...r,meta:t}}function G(r){return JSON.stringify(r!=null?r:null)}import{jsx as ue,jsxs as He}from"react/jsx-runtime";var _e="allow-scripts allow-same-origin allow-forms",Re="<h1>Hello World</h1><p>Start building your email template.</p>",he="https://pc-email-template-builder.netlify.app",Be={position:"absolute",inset:0,display:"flex",alignItems:"center",justifyContent:"center",fontSize:"0.875rem",fontWeight:500,background:"linear-gradient(135deg, rgba(9,9,9,0.65), rgba(33,33,33,0.85))",color:"#fff",zIndex:2},be={border:"none",width:"100%",height:"100%"};function Oe(r,n){if(!n)return r;try{let t=new URL(r,typeof window!="undefined"?window.location.href:"https://example.com");return t.searchParams.set("preview","true"),t.searchParams.set("previewOnly","true"),t.toString()}catch{let t=r.includes("?")?"&":"?";return`${r}${t}preview=true&previewOnly=true`}}function Ce(r,n){if(!n||!n.trim())return r;try{let t=new URL(r,typeof window!="undefined"?window.location.href:"https://example.com");return t.searchParams.set("embedToken",n),t.toString()}catch{let t=r.includes("?")?"&":"?";return`${r}${t}embedToken=${encodeURIComponent(n)}`}}function xe({src:r,initialHtml:n,mergeTags:t,mergeTagTrigger:p,embedToken:z,templateId:T,config:w,showFooter:x,includeUnsubscribe:H,externalFooterHtml:g,footerInjectionMode:q,preview:ce=!1,previewOnly:de=!1,className:pe,style:fe,hideLoadingOverlay:Se=!1,iframeTitle:ye="Email Builder",sandbox:Ee=_e,onChange:I,onLoad:D,onSave:P,onUpload:k,onListAssets:v,onDeleteAsset:N,onReady:U,onStatusChange:_,onAuthError:E,allowedOrigin:F},me){let R=(r==null?void 0:r.trim())||he,h=ce||de,W=j(()=>h?{...w!=null?w:{},preview:!0,previewOnly:!0}:w,[w,h]),ge=j(()=>Ce(Oe(R,h),z),[R,z,h]),B=S(null),c=S(null),y=S(!1),b=S("loading"),[Te,J]=le("loading"),[we,Ae]=le(0),O=S([]),A=S(null),d=S(null),s=S(null),V=typeof n=="string"?n:T?"":Re,Q=q||(g&&g.trim()?"sdk":"default"),X={html:V,...Array.isArray(t)?{mergeTags:t}:{},...typeof p=="string"&&p.trim()?{mergeTagTrigger:p.trim()}:{},config:W,...T?{templateId:T}:{},showFooter:x,includeUnsubscribe:H,...g?{externalFooterHtml:g}:{},footerInjectionMode:Q},Z=S(G(X)),M=S({type:"INIT",payload:X}),ke=j(()=>oe(R,F),[R,F]),C=f(()=>"*",[]),m=f(e=>{b.current!==e&&(b.current=e,J(e),_==null||_(e))},[_]),u=f((e,i)=>{var o,l;let a=(l=(o=B.current)==null?void 0:o.contentWindow)!=null?l:c.current;if(a&&(c.current=a),!c.current||!y.current){O.current.push({message:e,correlationId:i});return}c.current.postMessage(L(e,i),C())},[C]),ee=f(()=>{if(!y.current||!c.current)return;let e=[...O.current];O.current=[],e.forEach(({message:i,correlationId:a})=>{var o;(o=c.current)==null||o.postMessage(L(i,a),C())})},[C]),te=f(()=>{y.current||(d.current&&(window.clearTimeout(d.current),d.current=null),s.current&&(window.clearInterval(s.current),s.current=null),y.current=!0,m("ready"),U==null||U(),u(M.current),ee())},[ee,U,u,m]),re=f(async e=>{var i,a;if(e.type==="UPLOAD"){if(!k){console.warn("[EmailBuilderSDK] Upload requested but no onUpload handler is configured.");return}try{let o=await k(e.payload.file);u({type:"UPLOAD_SUCCESS",payload:{url:o}},(i=e.meta)==null?void 0:i.id)}catch(o){console.error("[EmailBuilderSDK] Upload handler failed",o),u({type:"UPLOAD_SUCCESS",payload:{url:""}},(a=e.meta)==null?void 0:a.id)}}},[k,u]),ae=f(async e=>{var i,a,o;if(e.type==="LIST_ASSETS"){if(!v){u({type:"ASSETS_LIST",payload:{assets:[]}},(i=e.meta)==null?void 0:i.id);return}try{let l=await v(e.payload);u({type:"ASSETS_LIST",payload:{assets:Array.isArray(l)?l:[]}},(a=e.meta)==null?void 0:a.id)}catch(l){console.error("[EmailBuilderSDK] list assets handler failed",l),u({type:"ASSETS_LIST",payload:{assets:[]}},(o=e.meta)==null?void 0:o.id)}}},[v,u]),ne=f(async e=>{var i,a,o;if(e.type==="DELETE_ASSET"){if(!N){u({type:"DELETE_ASSET_SUCCESS",payload:{success:!1}},(i=e.meta)==null?void 0:i.id);return}try{let l=await N(e.payload||{});u({type:"DELETE_ASSET_SUCCESS",payload:{success:!!l}},(a=e.meta)==null?void 0:a.id)}catch(l){console.error("[EmailBuilderSDK] delete asset handler failed",l),u({type:"DELETE_ASSET_SUCCESS",payload:{success:!1}},(o=e.meta)==null?void 0:o.id)}}},[N,u]),K=f(e=>{var o,l,se;let i=(l=(o=B.current)==null?void 0:o.contentWindow)!=null?l:c.current;if(!i||e.source!==i||!Y(e.data))return;if(!A.current)A.current=e.origin;else if(e.origin!==A.current)return;let a=e.data;switch(e.source&&e.source!==c.current&&(c.current=e.source),a.type){case"READY":te();break;case"CHANGE":I==null||I(a.payload.html);break;case"LOADED":D==null||D(a.payload.html);break;case"SAVE":P==null||P(a.payload.html);break;case"UPLOAD":re(a);break;case"LIST_ASSETS":ae(a);break;case"DELETE_ASSET":ne(a);break;case"AUTH_ERROR":{let Le=((se=a.payload)==null?void 0:se.message)||"Email builder authentication failed";b.current!=="error"&&(b.current="error",J("error"),E==null||E(Le));break}default:break}},[te,re,ae,ne,I,D,P,E,m]);$(()=>{if(typeof window!="undefined")return window.addEventListener("message",K),()=>{window.removeEventListener("message",K)}},[K]),$(()=>{let e={html:V,...Array.isArray(t)?{mergeTags:t}:{},...typeof p=="string"&&p.trim()?{mergeTagTrigger:p.trim()}:{},config:W,...T?{templateId:T}:{},showFooter:x,includeUnsubscribe:H,...g!==void 0?{externalFooterHtml:g}:{},footerInjectionMode:Q},i=G(e);M.current={type:"INIT",payload:e},i!==Z.current&&y.current&&u(M.current),Z.current=i},[W,V,t,p,u,T,x,H,g,q]);let Me=f(()=>{var a,o;if(c.current=(o=(a=B.current)==null?void 0:a.contentWindow)!=null?o:null,y.current=!1,A.current=null,m("loading"),c.current)try{c.current.postMessage(L(M.current),"*")}catch{}d.current&&window.clearTimeout(d.current),s.current&&window.clearInterval(s.current);let e=0,i=12;s.current=window.setInterval(()=>{var l;if(y.current){s.current&&(window.clearInterval(s.current),s.current=null);return}e+=1;try{(l=c.current)==null||l.postMessage(L(M.current),"*")}catch{}e>=i&&s.current&&(window.clearInterval(s.current),s.current=null)},1e3),d.current=window.setTimeout(()=>{y.current||(s.current&&(window.clearInterval(s.current),s.current=null),m("error"),E==null||E("Builder handshake failed or authentication was rejected."))},12e3)},[E,m]);return Ue(me,()=>({reload(){O.current=[],y.current=!1,c.current=null,A.current=null,d.current&&(window.clearTimeout(d.current),d.current=null),s.current&&(window.clearInterval(s.current),s.current=null),m("loading"),Ae(e=>e+1)}}),[m]),$(()=>()=>{d.current&&(window.clearTimeout(d.current),d.current=null),s.current&&(window.clearInterval(s.current),s.current=null)},[]),He("div",{className:pe,style:{position:"relative",width:"100%",height:"100%",...fe},children:[ue("iframe",{ref:B,src:ge,title:ye,sandbox:Ee,style:be,loading:"lazy",allowFullScreen:!0,onLoad:Me},we),!Se&&Te!=="ready"&&ue("div",{style:Be,children:"Connecting to builder\u2026"})]})}var ze=Pe(xe);export{Ie as EMAIL_BUILDER_PROTOCOL_VERSION,ze as EmailBuilder,ie as createMessageMeta,Y as isMessageLike};
|
|
2
2
|
//# sourceMappingURL=index.js.map
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/EmailBuilder.tsx","../src/protocol.ts","../src/utils.ts"],"sourcesContent":["import React, {\n CSSProperties,\n ForwardedRef,\n forwardRef,\n useCallback,\n useEffect,\n useImperativeHandle,\n useMemo,\n useRef,\n useState,\n} from 'react';\nimport type { BuilderToHostMessage, HostToBuilderMessage, InitPayload } from './protocol';\nimport { isMessageLike } from './protocol';\nimport { buildEnvelope, deriveAllowedOrigin, stableSignature } from './utils';\nimport type { EmailBuilderHandle, EmailBuilderProps, EmailBuilderStatus } from './types';\n\nconst DEFAULT_SANDBOX = 'allow-scripts allow-same-origin allow-forms';\nconst DEFAULT_INITIAL_HTML = '<h1>Hello World</h1><p>Start building your email template.</p>';\nconst DEFAULT_BUILDER_SRC = 'https://pc-email-template-builder.netlify.app';\n\ntype PendingMessage = {\n message: HostToBuilderMessage;\n correlationId?: string;\n};\n\nconst overlayStyle: CSSProperties = {\n position: 'absolute',\n inset: 0,\n display: 'flex',\n alignItems: 'center',\n justifyContent: 'center',\n fontSize: '0.875rem',\n fontWeight: 500,\n background: 'linear-gradient(135deg, rgba(9,9,9,0.65), rgba(33,33,33,0.85))',\n color: '#fff',\n zIndex: 2,\n};\n\nconst iframeStyle: CSSProperties = {\n border: 'none',\n width: '100%',\n height: '100%',\n};\n\nfunction appendPreviewParamsToSrc(src: string, preview: boolean): string {\n if (!preview) {\n return src;\n }\n try {\n const u = new URL(src, typeof window !== 'undefined' ? window.location.href : 'https://example.com');\n u.searchParams.set('preview', 'true');\n u.searchParams.set('previewOnly', 'true');\n return u.toString();\n } catch {\n const sep = src.includes('?') ? '&' : '?';\n return `${src}${sep}preview=true&previewOnly=true`;\n }\n}\n\nfunction appendEmbedTokenToSrc(src: string, embedToken: string | undefined): string {\n if (!embedToken || !embedToken.trim()) {\n return src;\n }\n try {\n const u = new URL(src, typeof window !== 'undefined' ? window.location.href : 'https://example.com');\n u.searchParams.set('embedToken', embedToken);\n return u.toString();\n } catch {\n const sep = src.includes('?') ? '&' : '?';\n return `${src}${sep}embedToken=${encodeURIComponent(embedToken)}`;\n }\n}\n\nfunction EmailBuilderInner(\n {\n src,\n initialHtml,\n embedToken,\n templateId,\n config,\n showFooter,\n includeUnsubscribe,\n externalFooterHtml,\n footerInjectionMode,\n preview = false,\n previewOnly = false,\n className,\n style,\n iframeTitle = 'Email Builder',\n sandbox = DEFAULT_SANDBOX,\n onChange,\n onLoad,\n onSave,\n onUpload,\n onListAssets,\n onDeleteAsset,\n onReady,\n onStatusChange,\n onAuthError,\n allowedOrigin,\n }: EmailBuilderProps,\n ref: ForwardedRef<EmailBuilderHandle>\n) {\n const resolvedSrc = src?.trim() || DEFAULT_BUILDER_SRC;\n const isPreviewMode = preview || previewOnly;\n const effectiveConfig = useMemo(\n () => (isPreviewMode ? { ...(config ?? {}), preview: true, previewOnly: true } : config),\n [config, isPreviewMode]\n );\n const iframeSrc = useMemo(\n () => appendEmbedTokenToSrc(appendPreviewParamsToSrc(resolvedSrc, isPreviewMode), embedToken),\n [resolvedSrc, embedToken, isPreviewMode]\n );\n\n const iframeRef = useRef<HTMLIFrameElement>(null);\n const builderWindowRef = useRef<Window | null>(null);\n const readyRef = useRef(false);\n const statusRef = useRef<EmailBuilderStatus>('loading');\n const [status, setStatus] = useState<EmailBuilderStatus>('loading');\n const [reloadKey, setReloadKey] = useState(0);\n const queueRef = useRef<PendingMessage[]>([]);\n const runtimeOriginRef = useRef<string | null>(null);\n const handshakeTimerRef = useRef<number | null>(null);\n const handshakeRetryIntervalRef = useRef<number | null>(null);\n\n const effectiveInitialHtml =\n typeof initialHtml === 'string'\n ? initialHtml\n : templateId\n ? ''\n : DEFAULT_INITIAL_HTML;\n\n const effectiveFooterMode =\n footerInjectionMode ||\n (externalFooterHtml && externalFooterHtml.trim() ? 'sdk' : 'default');\n\n const initPayload: InitPayload = {\n html: effectiveInitialHtml,\n config: effectiveConfig,\n ...(templateId ? { templateId } : {}),\n showFooter,\n includeUnsubscribe,\n ...(externalFooterHtml ? { externalFooterHtml } : {}),\n footerInjectionMode: effectiveFooterMode,\n };\n\n const initSignatureRef = useRef(stableSignature(initPayload));\n const latestInitRef = useRef<HostToBuilderMessage>({\n type: 'INIT',\n payload: initPayload,\n });\n\n const expectedOrigin = useMemo(() => deriveAllowedOrigin(resolvedSrc, allowedOrigin), [resolvedSrc, allowedOrigin]);\n\n const resolveTargetOrigin = useCallback(() => '*', []);\n\n const setStatusSafely = useCallback(\n (next: EmailBuilderStatus) => {\n if (statusRef.current === next) {\n return;\n }\n statusRef.current = next;\n setStatus(next);\n onStatusChange?.(next);\n },\n [onStatusChange]\n );\n\n const postMessage = useCallback(\n (message: HostToBuilderMessage, correlationId?: string) => {\n const target = iframeRef.current?.contentWindow ?? builderWindowRef.current;\n if (target) {\n builderWindowRef.current = target;\n }\n\n if (!builderWindowRef.current || !readyRef.current) {\n queueRef.current.push({ message, correlationId });\n return;\n }\n\n builderWindowRef.current.postMessage(buildEnvelope(message, correlationId), resolveTargetOrigin());\n },\n [resolveTargetOrigin]\n );\n\n const flushQueue = useCallback(() => {\n if (!readyRef.current || !builderWindowRef.current) {\n return;\n }\n const pending = [...queueRef.current];\n queueRef.current = [];\n pending.forEach(({ message, correlationId }) => {\n builderWindowRef.current?.postMessage(\n buildEnvelope(message, correlationId),\n resolveTargetOrigin()\n );\n });\n }, [resolveTargetOrigin]);\n\n const handleReadyMessage = useCallback(() => {\n if (readyRef.current) {\n return;\n }\n if (handshakeTimerRef.current) {\n window.clearTimeout(handshakeTimerRef.current);\n handshakeTimerRef.current = null;\n }\n if (handshakeRetryIntervalRef.current) {\n window.clearInterval(handshakeRetryIntervalRef.current);\n handshakeRetryIntervalRef.current = null;\n }\n readyRef.current = true;\n setStatusSafely('ready');\n onReady?.();\n postMessage(latestInitRef.current);\n flushQueue();\n }, [flushQueue, onReady, postMessage, setStatusSafely]);\n\n const handleUpload = useCallback(\n async (eventMessage: BuilderToHostMessage) => {\n if (eventMessage.type !== 'UPLOAD') {\n return;\n }\n if (!onUpload) {\n console.warn('[EmailBuilderSDK] Upload requested but no onUpload handler is configured.');\n return;\n }\n try {\n const url = await onUpload(eventMessage.payload.file);\n postMessage({ type: 'UPLOAD_SUCCESS', payload: { url } }, eventMessage.meta?.id);\n } catch (error) {\n console.error('[EmailBuilderSDK] Upload handler failed', error);\n // Keep builder session ready; upload failure should not flip global iframe status.\n postMessage({ type: 'UPLOAD_SUCCESS', payload: { url: '' } }, eventMessage.meta?.id);\n }\n },\n [onUpload, postMessage]\n );\n\n const handleListAssets = useCallback(\n async (eventMessage: BuilderToHostMessage) => {\n if (eventMessage.type !== 'LIST_ASSETS') {\n return;\n }\n if (!onListAssets) {\n postMessage({ type: 'ASSETS_LIST', payload: { assets: [] } }, eventMessage.meta?.id);\n return;\n }\n try {\n const assets = await onListAssets(eventMessage.payload);\n postMessage(\n { type: 'ASSETS_LIST', payload: { assets: Array.isArray(assets) ? assets : [] } },\n eventMessage.meta?.id\n );\n } catch (error) {\n console.error('[EmailBuilderSDK] list assets handler failed', error);\n postMessage({ type: 'ASSETS_LIST', payload: { assets: [] } }, eventMessage.meta?.id);\n }\n },\n [onListAssets, postMessage]\n );\n\n const handleDeleteAsset = useCallback(\n async (eventMessage: BuilderToHostMessage) => {\n if (eventMessage.type !== 'DELETE_ASSET') {\n return;\n }\n if (!onDeleteAsset) {\n postMessage({ type: 'DELETE_ASSET_SUCCESS', payload: { success: false } }, eventMessage.meta?.id);\n return;\n }\n try {\n const success = await onDeleteAsset(eventMessage.payload || {});\n postMessage({ type: 'DELETE_ASSET_SUCCESS', payload: { success: !!success } }, eventMessage.meta?.id);\n } catch (error) {\n console.error('[EmailBuilderSDK] delete asset handler failed', error);\n postMessage({ type: 'DELETE_ASSET_SUCCESS', payload: { success: false } }, eventMessage.meta?.id);\n }\n },\n [onDeleteAsset, postMessage]\n );\n\n const handleMessage = useCallback(\n (event: MessageEvent) => {\n const iframeWindow = iframeRef.current?.contentWindow ?? builderWindowRef.current;\n if (!iframeWindow || event.source !== iframeWindow) {\n return;\n }\n if (!isMessageLike(event.data)) {\n return;\n }\n if (!runtimeOriginRef.current) {\n runtimeOriginRef.current = event.origin;\n } else if (event.origin !== runtimeOriginRef.current) {\n return;\n }\n const message = event.data as BuilderToHostMessage;\n\n if (event.source && event.source !== builderWindowRef.current) {\n builderWindowRef.current = event.source as Window;\n }\n\n switch (message.type) {\n case 'READY':\n handleReadyMessage();\n break;\n case 'CHANGE':\n onChange?.(message.payload.html);\n break;\n case 'LOADED':\n onLoad?.(message.payload.html);\n break;\n case 'SAVE':\n onSave?.(message.payload.html);\n break;\n case 'UPLOAD':\n void handleUpload(message);\n break;\n case 'LIST_ASSETS':\n void handleListAssets(message);\n break;\n case 'DELETE_ASSET':\n void handleDeleteAsset(message);\n break;\n case 'AUTH_ERROR': {\n const msg = message.payload?.message || 'Email builder authentication failed';\n if (statusRef.current !== 'error') {\n statusRef.current = 'error';\n setStatus('error');\n onAuthError?.(msg);\n }\n break;\n }\n default:\n break;\n }\n },\n [handleReadyMessage, handleUpload, handleListAssets, handleDeleteAsset, onChange, onLoad, onSave, onAuthError, setStatusSafely]\n );\n\n useEffect(() => {\n if (typeof window === 'undefined') {\n return;\n }\n window.addEventListener('message', handleMessage);\n return () => {\n window.removeEventListener('message', handleMessage);\n };\n }, [handleMessage]);\n\n useEffect(() => {\n const nextPayload: InitPayload = {\n html: effectiveInitialHtml,\n config: effectiveConfig,\n ...(templateId ? { templateId } : {}),\n showFooter,\n includeUnsubscribe,\n ...(externalFooterHtml !== undefined ? { externalFooterHtml } : {}),\n footerInjectionMode: effectiveFooterMode,\n };\n const signature = stableSignature(nextPayload);\n latestInitRef.current = { type: 'INIT', payload: nextPayload };\n if (signature !== initSignatureRef.current && readyRef.current) {\n postMessage(latestInitRef.current);\n }\n initSignatureRef.current = signature;\n }, [\n effectiveConfig,\n effectiveInitialHtml,\n postMessage,\n templateId,\n showFooter,\n includeUnsubscribe,\n externalFooterHtml,\n footerInjectionMode,\n ]);\n\n const handleIframeLoad = useCallback(() => {\n builderWindowRef.current = iframeRef.current?.contentWindow ?? null;\n readyRef.current = false;\n runtimeOriginRef.current = null;\n setStatusSafely('loading');\n\n if (builderWindowRef.current) {\n try {\n builderWindowRef.current.postMessage(buildEnvelope(latestInitRef.current), '*');\n } catch {\n // Ignore and wait for normal READY/message flow.\n }\n }\n\n if (handshakeTimerRef.current) {\n window.clearTimeout(handshakeTimerRef.current);\n }\n if (handshakeRetryIntervalRef.current) {\n window.clearInterval(handshakeRetryIntervalRef.current);\n }\n\n let attempts = 0;\n const maxAttempts = 12;\n handshakeRetryIntervalRef.current = window.setInterval(() => {\n if (readyRef.current) {\n if (handshakeRetryIntervalRef.current) {\n window.clearInterval(handshakeRetryIntervalRef.current);\n handshakeRetryIntervalRef.current = null;\n }\n return;\n }\n attempts += 1;\n try {\n builderWindowRef.current?.postMessage(buildEnvelope(latestInitRef.current), '*');\n } catch {\n // Keep retrying until timeout.\n }\n if (attempts >= maxAttempts) {\n if (handshakeRetryIntervalRef.current) {\n window.clearInterval(handshakeRetryIntervalRef.current);\n handshakeRetryIntervalRef.current = null;\n }\n }\n }, 1000);\n\n handshakeTimerRef.current = window.setTimeout(() => {\n if (readyRef.current) {\n return;\n }\n if (handshakeRetryIntervalRef.current) {\n window.clearInterval(handshakeRetryIntervalRef.current);\n handshakeRetryIntervalRef.current = null;\n }\n setStatusSafely('error');\n onAuthError?.('Builder handshake failed or authentication was rejected.');\n }, 12000);\n }, [onAuthError, setStatusSafely]);\n\n useImperativeHandle(\n ref,\n () => ({\n reload() {\n queueRef.current = [];\n readyRef.current = false;\n builderWindowRef.current = null;\n runtimeOriginRef.current = null;\n if (handshakeTimerRef.current) {\n window.clearTimeout(handshakeTimerRef.current);\n handshakeTimerRef.current = null;\n }\n if (handshakeRetryIntervalRef.current) {\n window.clearInterval(handshakeRetryIntervalRef.current);\n handshakeRetryIntervalRef.current = null;\n }\n setStatusSafely('loading');\n setReloadKey((key) => key + 1);\n },\n }),\n [setStatusSafely]\n );\n\n useEffect(() => {\n return () => {\n if (handshakeTimerRef.current) {\n window.clearTimeout(handshakeTimerRef.current);\n handshakeTimerRef.current = null;\n }\n if (handshakeRetryIntervalRef.current) {\n window.clearInterval(handshakeRetryIntervalRef.current);\n handshakeRetryIntervalRef.current = null;\n }\n };\n }, []);\n\n return (\n <div className={className} style={{ position: 'relative', width: '100%', height: '100%', ...style }}>\n <iframe\n key={reloadKey}\n ref={iframeRef}\n src={iframeSrc}\n title={iframeTitle}\n sandbox={sandbox}\n style={iframeStyle}\n loading=\"lazy\"\n allowFullScreen\n onLoad={handleIframeLoad}\n />\n {status !== 'ready' && (\n <div style={overlayStyle}>Connecting to builder…</div>\n )}\n </div>\n );\n}\n\nexport const EmailBuilder = forwardRef(EmailBuilderInner);\n","export const EMAIL_BUILDER_PROTOCOL_VERSION = '1.0.0';\n\nexport type MessageType =\n | 'INIT'\n | 'READY'\n | 'CHANGE'\n | 'LOADED'\n | 'SAVE'\n | 'UPLOAD'\n | 'UPLOAD_SUCCESS'\n | 'LIST_ASSETS'\n | 'ASSETS_LIST'\n | 'DELETE_ASSET'\n | 'DELETE_ASSET_SUCCESS'\n | 'AUTH_ERROR';\n\nexport interface MessageMeta {\n id: string;\n correlationId?: string;\n version: string;\n sentAt: number;\n}\n\nexport type BuilderConfig = Record<string, unknown> | undefined;\n\nexport interface InitPayload {\n /** Inline HTML to import (optional if templateId is set and builder fetches server-side). */\n html?: string;\n /** When set, embedded builder fetches HTML from API using embed token + this id. */\n templateId?: string;\n config?: BuilderConfig;\n /**\n * Optional: host-provided footer HTML to be rendered by the builder (SDK mode).\n * If set, the builder can avoid importing its own footer and render this exact footer instead.\n */\n externalFooterHtml?: string;\n /**\n * Footer source selection.\n * - `default`: builder uses its existing behavior.\n * - `sdk`: builder uses `externalFooterHtml` (and should avoid importing/auto-generating footer).\n */\n footerInjectionMode?: 'default' | 'sdk';\n /** Show \"Powered by Public Circles\" branding in footer. Controlled by host based on add-on purchase. */\n showFooter?: boolean;\n /** Include unsubscribe link in footer. Controlled by host toggle. */\n includeUnsubscribe?: boolean;\n}\n\nexport interface ChangePayload {\n html: string;\n}\n\nexport interface LoadedPayload {\n html: string;\n}\n\nexport interface SavePayload {\n html: string;\n}\n\nexport interface UploadPayload {\n file: File;\n}\n\nexport interface UploadSuccessPayload {\n url: string;\n}\n\nexport interface AssetItem {\n url: string;\n id?: string;\n name?: string;\n thumbnailUrl?: string;\n}\n\nexport interface ListAssetsPayload {\n limit?: number;\n}\n\nexport interface AssetsListPayload {\n assets: AssetItem[];\n}\n\nexport interface DeleteAssetPayload {\n id?: string;\n url?: string;\n}\n\nexport interface DeleteAssetSuccessPayload {\n success: boolean;\n}\n\nexport interface AuthErrorPayload {\n message: string;\n}\n\nexport type Message =\n | { type: 'INIT'; payload: InitPayload; meta?: MessageMeta }\n | { type: 'READY'; meta?: MessageMeta }\n | { type: 'CHANGE'; payload: ChangePayload; meta?: MessageMeta }\n | { type: 'LOADED'; payload: LoadedPayload; meta?: MessageMeta }\n | { type: 'SAVE'; payload: SavePayload; meta?: MessageMeta }\n | { type: 'UPLOAD'; payload: UploadPayload; meta?: MessageMeta }\n | { type: 'UPLOAD_SUCCESS'; payload: UploadSuccessPayload; meta?: MessageMeta }\n | { type: 'LIST_ASSETS'; payload?: ListAssetsPayload; meta?: MessageMeta }\n | { type: 'ASSETS_LIST'; payload: AssetsListPayload; meta?: MessageMeta }\n | { type: 'DELETE_ASSET'; payload: DeleteAssetPayload; meta?: MessageMeta }\n | { type: 'DELETE_ASSET_SUCCESS'; payload: DeleteAssetSuccessPayload; meta?: MessageMeta }\n | { type: 'AUTH_ERROR'; payload: AuthErrorPayload; meta?: MessageMeta };\n\nexport type BuilderToHostMessage = Extract<\n Message,\n { type: 'READY' | 'CHANGE' | 'LOADED' | 'SAVE' | 'UPLOAD' | 'LIST_ASSETS' | 'DELETE_ASSET' | 'AUTH_ERROR' }\n>;\n\nexport type HostToBuilderMessage = Extract<\n Message,\n { type: 'INIT' | 'UPLOAD_SUCCESS' | 'ASSETS_LIST' | 'DELETE_ASSET_SUCCESS' }\n>;\n\nconst VALID_TYPES: MessageType[] = [\n 'INIT',\n 'READY',\n 'CHANGE',\n 'LOADED',\n 'SAVE',\n 'UPLOAD',\n 'UPLOAD_SUCCESS',\n 'LIST_ASSETS',\n 'ASSETS_LIST',\n 'DELETE_ASSET',\n 'DELETE_ASSET_SUCCESS',\n 'AUTH_ERROR',\n];\n\nexport function isMessageLike(value: unknown): value is Message {\n if (typeof value !== 'object' || value === null) {\n return false;\n }\n\n const candidate = value as { type?: unknown; payload?: unknown; meta?: unknown };\n if (typeof candidate.type !== 'string' || !VALID_TYPES.includes(candidate.type as MessageType)) {\n return false;\n }\n\n if ('meta' in candidate && candidate.meta !== undefined) {\n const meta = candidate.meta as Partial<MessageMeta>;\n if (\n (meta.id && typeof meta.id !== 'string') ||\n (meta.correlationId && typeof meta.correlationId !== 'string') ||\n (meta.version && typeof meta.version !== 'string') ||\n (meta.sentAt && typeof meta.sentAt !== 'number')\n ) {\n return false;\n }\n }\n\n return true;\n}\n\nexport function createMessageMeta(correlationId?: string): MessageMeta {\n const id = typeof crypto !== 'undefined' && 'randomUUID' in crypto\n ? crypto.randomUUID()\n : Math.random().toString(36).slice(2);\n return {\n id,\n correlationId,\n version: EMAIL_BUILDER_PROTOCOL_VERSION,\n sentAt: Date.now(),\n };\n}\n","import type { BuilderToHostMessage, HostToBuilderMessage, MessageMeta } from './protocol';\nimport { createMessageMeta, isMessageLike } from './protocol';\n\nexport function deriveAllowedOrigin(src: string, override?: string): string {\n if (override) {\n const parsed = new URL(override);\n if (parsed.origin === 'null') {\n throw new Error('allowedOrigin must resolve to a valid origin');\n }\n return parsed.origin;\n }\n\n let parsed: URL;\n try {\n parsed = new URL(src);\n } catch {\n throw new Error('EmailBuilder `src` must be an absolute URL (e.g. https://example.com)');\n }\n if (!parsed.origin || parsed.origin === 'null') {\n throw new Error('EmailBuilder requires an absolute src URL with a valid origin');\n }\n return parsed.origin;\n}\n\nexport function buildEnvelope<T extends HostToBuilderMessage>(message: T, correlationId?: string): T {\n const meta: MessageMeta = createMessageMeta(correlationId);\n return { ...message, meta } as T;\n}\n\nexport function sanitizeIncomingMessage(\n event: MessageEvent,\n allowedOrigin: string,\n iframeWindow: Window | null\n): BuilderToHostMessage | null {\n if (event.origin !== allowedOrigin) {\n return null;\n }\n\n if (!iframeWindow || event.source !== iframeWindow) {\n return null;\n }\n\n if (!isMessageLike(event.data)) {\n return null;\n }\n\n return event.data as BuilderToHostMessage;\n}\n\nexport function stableSignature(value: unknown): string {\n return JSON.stringify(value ?? null);\n}\n"],"mappings":"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","names":["forwardRef","useCallback","useEffect","useImperativeHandle","useMemo","useRef","useState","EMAIL_BUILDER_PROTOCOL_VERSION","VALID_TYPES","isMessageLike","value","candidate","meta","createMessageMeta","correlationId","deriveAllowedOrigin","src","override","parsed","buildEnvelope","message","correlationId","meta","createMessageMeta","stableSignature","value","jsx","jsxs","DEFAULT_SANDBOX","DEFAULT_INITIAL_HTML","DEFAULT_BUILDER_SRC","overlayStyle","iframeStyle","appendPreviewParamsToSrc","src","preview","u","sep","appendEmbedTokenToSrc","embedToken","EmailBuilderInner","initialHtml","templateId","config","showFooter","includeUnsubscribe","externalFooterHtml","footerInjectionMode","previewOnly","className","style","iframeTitle","sandbox","onChange","onLoad","onSave","onUpload","onListAssets","onDeleteAsset","onReady","onStatusChange","onAuthError","allowedOrigin","ref","resolvedSrc","isPreviewMode","effectiveConfig","useMemo","iframeSrc","iframeRef","useRef","builderWindowRef","readyRef","statusRef","status","setStatus","useState","reloadKey","setReloadKey","queueRef","runtimeOriginRef","handshakeTimerRef","handshakeRetryIntervalRef","effectiveInitialHtml","effectiveFooterMode","initPayload","initSignatureRef","stableSignature","latestInitRef","expectedOrigin","deriveAllowedOrigin","resolveTargetOrigin","useCallback","setStatusSafely","next","postMessage","message","correlationId","_a","_b","target","buildEnvelope","flushQueue","pending","handleReadyMessage","handleUpload","eventMessage","url","error","handleListAssets","_c","assets","handleDeleteAsset","success","handleMessage","event","iframeWindow","isMessageLike","msg","useEffect","nextPayload","signature","handleIframeLoad","attempts","maxAttempts","useImperativeHandle","key","EmailBuilder","forwardRef"]}
|
|
1
|
+
{"version":3,"sources":["../src/EmailBuilder.tsx","../src/protocol.ts","../src/utils.ts"],"sourcesContent":["import React, {\n CSSProperties,\n ForwardedRef,\n forwardRef,\n useCallback,\n useEffect,\n useImperativeHandle,\n useMemo,\n useRef,\n useState,\n} from 'react';\nimport type { BuilderToHostMessage, HostToBuilderMessage, InitPayload } from './protocol';\nimport { isMessageLike } from './protocol';\nimport { buildEnvelope, deriveAllowedOrigin, stableSignature } from './utils';\nimport type { EmailBuilderHandle, EmailBuilderProps, EmailBuilderStatus } from './types';\n\nconst DEFAULT_SANDBOX = 'allow-scripts allow-same-origin allow-forms';\nconst DEFAULT_INITIAL_HTML = '<h1>Hello World</h1><p>Start building your email template.</p>';\nconst DEFAULT_BUILDER_SRC = 'https://pc-email-template-builder.netlify.app';\n\ntype PendingMessage = {\n message: HostToBuilderMessage;\n correlationId?: string;\n};\n\nconst overlayStyle: CSSProperties = {\n position: 'absolute',\n inset: 0,\n display: 'flex',\n alignItems: 'center',\n justifyContent: 'center',\n fontSize: '0.875rem',\n fontWeight: 500,\n background: 'linear-gradient(135deg, rgba(9,9,9,0.65), rgba(33,33,33,0.85))',\n color: '#fff',\n zIndex: 2,\n};\n\nconst iframeStyle: CSSProperties = {\n border: 'none',\n width: '100%',\n height: '100%',\n};\n\nfunction appendPreviewParamsToSrc(src: string, preview: boolean): string {\n if (!preview) {\n return src;\n }\n try {\n const u = new URL(src, typeof window !== 'undefined' ? window.location.href : 'https://example.com');\n u.searchParams.set('preview', 'true');\n u.searchParams.set('previewOnly', 'true');\n return u.toString();\n } catch {\n const sep = src.includes('?') ? '&' : '?';\n return `${src}${sep}preview=true&previewOnly=true`;\n }\n}\n\nfunction appendEmbedTokenToSrc(src: string, embedToken: string | undefined): string {\n if (!embedToken || !embedToken.trim()) {\n return src;\n }\n try {\n const u = new URL(src, typeof window !== 'undefined' ? window.location.href : 'https://example.com');\n u.searchParams.set('embedToken', embedToken);\n return u.toString();\n } catch {\n const sep = src.includes('?') ? '&' : '?';\n return `${src}${sep}embedToken=${encodeURIComponent(embedToken)}`;\n }\n}\n\nfunction EmailBuilderInner(\n {\n src,\n initialHtml,\n mergeTags,\n mergeTagTrigger,\n embedToken,\n templateId,\n config,\n showFooter,\n includeUnsubscribe,\n externalFooterHtml,\n footerInjectionMode,\n preview = false,\n previewOnly = false,\n className,\n style,\n hideLoadingOverlay = false,\n iframeTitle = 'Email Builder',\n sandbox = DEFAULT_SANDBOX,\n onChange,\n onLoad,\n onSave,\n onUpload,\n onListAssets,\n onDeleteAsset,\n onReady,\n onStatusChange,\n onAuthError,\n allowedOrigin,\n }: EmailBuilderProps,\n ref: ForwardedRef<EmailBuilderHandle>\n) {\n const resolvedSrc = src?.trim() || DEFAULT_BUILDER_SRC;\n const isPreviewMode = preview || previewOnly;\n const effectiveConfig = useMemo(\n () => (isPreviewMode ? { ...(config ?? {}), preview: true, previewOnly: true } : config),\n [config, isPreviewMode]\n );\n const iframeSrc = useMemo(\n () => appendEmbedTokenToSrc(appendPreviewParamsToSrc(resolvedSrc, isPreviewMode), embedToken),\n [resolvedSrc, embedToken, isPreviewMode]\n );\n\n const iframeRef = useRef<HTMLIFrameElement>(null);\n const builderWindowRef = useRef<Window | null>(null);\n const readyRef = useRef(false);\n const statusRef = useRef<EmailBuilderStatus>('loading');\n const [status, setStatus] = useState<EmailBuilderStatus>('loading');\n const [reloadKey, setReloadKey] = useState(0);\n const queueRef = useRef<PendingMessage[]>([]);\n const runtimeOriginRef = useRef<string | null>(null);\n const handshakeTimerRef = useRef<number | null>(null);\n const handshakeRetryIntervalRef = useRef<number | null>(null);\n\n const effectiveInitialHtml =\n typeof initialHtml === 'string'\n ? initialHtml\n : templateId\n ? ''\n : DEFAULT_INITIAL_HTML;\n\n const effectiveFooterMode =\n footerInjectionMode ||\n (externalFooterHtml && externalFooterHtml.trim() ? 'sdk' : 'default');\n\n const initPayload: InitPayload = {\n html: effectiveInitialHtml,\n ...(Array.isArray(mergeTags) ? { mergeTags } : {}),\n ...(typeof mergeTagTrigger === 'string' && mergeTagTrigger.trim()\n ? { mergeTagTrigger: mergeTagTrigger.trim() }\n : {}),\n config: effectiveConfig,\n ...(templateId ? { templateId } : {}),\n showFooter,\n includeUnsubscribe,\n ...(externalFooterHtml ? { externalFooterHtml } : {}),\n footerInjectionMode: effectiveFooterMode,\n };\n\n const initSignatureRef = useRef(stableSignature(initPayload));\n const latestInitRef = useRef<HostToBuilderMessage>({\n type: 'INIT',\n payload: initPayload,\n });\n\n const expectedOrigin = useMemo(() => deriveAllowedOrigin(resolvedSrc, allowedOrigin), [resolvedSrc, allowedOrigin]);\n\n const resolveTargetOrigin = useCallback(() => '*', []);\n\n const setStatusSafely = useCallback(\n (next: EmailBuilderStatus) => {\n if (statusRef.current === next) {\n return;\n }\n statusRef.current = next;\n setStatus(next);\n onStatusChange?.(next);\n },\n [onStatusChange]\n );\n\n const postMessage = useCallback(\n (message: HostToBuilderMessage, correlationId?: string) => {\n const target = iframeRef.current?.contentWindow ?? builderWindowRef.current;\n if (target) {\n builderWindowRef.current = target;\n }\n\n if (!builderWindowRef.current || !readyRef.current) {\n queueRef.current.push({ message, correlationId });\n return;\n }\n\n builderWindowRef.current.postMessage(buildEnvelope(message, correlationId), resolveTargetOrigin());\n },\n [resolveTargetOrigin]\n );\n\n const flushQueue = useCallback(() => {\n if (!readyRef.current || !builderWindowRef.current) {\n return;\n }\n const pending = [...queueRef.current];\n queueRef.current = [];\n pending.forEach(({ message, correlationId }) => {\n builderWindowRef.current?.postMessage(\n buildEnvelope(message, correlationId),\n resolveTargetOrigin()\n );\n });\n }, [resolveTargetOrigin]);\n\n const handleReadyMessage = useCallback(() => {\n if (readyRef.current) {\n return;\n }\n if (handshakeTimerRef.current) {\n window.clearTimeout(handshakeTimerRef.current);\n handshakeTimerRef.current = null;\n }\n if (handshakeRetryIntervalRef.current) {\n window.clearInterval(handshakeRetryIntervalRef.current);\n handshakeRetryIntervalRef.current = null;\n }\n readyRef.current = true;\n setStatusSafely('ready');\n onReady?.();\n postMessage(latestInitRef.current);\n flushQueue();\n }, [flushQueue, onReady, postMessage, setStatusSafely]);\n\n const handleUpload = useCallback(\n async (eventMessage: BuilderToHostMessage) => {\n if (eventMessage.type !== 'UPLOAD') {\n return;\n }\n if (!onUpload) {\n console.warn('[EmailBuilderSDK] Upload requested but no onUpload handler is configured.');\n return;\n }\n try {\n const url = await onUpload(eventMessage.payload.file);\n postMessage({ type: 'UPLOAD_SUCCESS', payload: { url } }, eventMessage.meta?.id);\n } catch (error) {\n console.error('[EmailBuilderSDK] Upload handler failed', error);\n // Keep builder session ready; upload failure should not flip global iframe status.\n postMessage({ type: 'UPLOAD_SUCCESS', payload: { url: '' } }, eventMessage.meta?.id);\n }\n },\n [onUpload, postMessage]\n );\n\n const handleListAssets = useCallback(\n async (eventMessage: BuilderToHostMessage) => {\n if (eventMessage.type !== 'LIST_ASSETS') {\n return;\n }\n if (!onListAssets) {\n postMessage({ type: 'ASSETS_LIST', payload: { assets: [] } }, eventMessage.meta?.id);\n return;\n }\n try {\n const assets = await onListAssets(eventMessage.payload);\n postMessage(\n { type: 'ASSETS_LIST', payload: { assets: Array.isArray(assets) ? assets : [] } },\n eventMessage.meta?.id\n );\n } catch (error) {\n console.error('[EmailBuilderSDK] list assets handler failed', error);\n postMessage({ type: 'ASSETS_LIST', payload: { assets: [] } }, eventMessage.meta?.id);\n }\n },\n [onListAssets, postMessage]\n );\n\n const handleDeleteAsset = useCallback(\n async (eventMessage: BuilderToHostMessage) => {\n if (eventMessage.type !== 'DELETE_ASSET') {\n return;\n }\n if (!onDeleteAsset) {\n postMessage({ type: 'DELETE_ASSET_SUCCESS', payload: { success: false } }, eventMessage.meta?.id);\n return;\n }\n try {\n const success = await onDeleteAsset(eventMessage.payload || {});\n postMessage({ type: 'DELETE_ASSET_SUCCESS', payload: { success: !!success } }, eventMessage.meta?.id);\n } catch (error) {\n console.error('[EmailBuilderSDK] delete asset handler failed', error);\n postMessage({ type: 'DELETE_ASSET_SUCCESS', payload: { success: false } }, eventMessage.meta?.id);\n }\n },\n [onDeleteAsset, postMessage]\n );\n\n const handleMessage = useCallback(\n (event: MessageEvent) => {\n const iframeWindow = iframeRef.current?.contentWindow ?? builderWindowRef.current;\n if (!iframeWindow || event.source !== iframeWindow) {\n return;\n }\n if (!isMessageLike(event.data)) {\n return;\n }\n if (!runtimeOriginRef.current) {\n runtimeOriginRef.current = event.origin;\n } else if (event.origin !== runtimeOriginRef.current) {\n return;\n }\n const message = event.data as BuilderToHostMessage;\n\n if (event.source && event.source !== builderWindowRef.current) {\n builderWindowRef.current = event.source as Window;\n }\n\n switch (message.type) {\n case 'READY':\n handleReadyMessage();\n break;\n case 'CHANGE':\n onChange?.(message.payload.html);\n break;\n case 'LOADED':\n onLoad?.(message.payload.html);\n break;\n case 'SAVE':\n onSave?.(message.payload.html);\n break;\n case 'UPLOAD':\n void handleUpload(message);\n break;\n case 'LIST_ASSETS':\n void handleListAssets(message);\n break;\n case 'DELETE_ASSET':\n void handleDeleteAsset(message);\n break;\n case 'AUTH_ERROR': {\n const msg = message.payload?.message || 'Email builder authentication failed';\n if (statusRef.current !== 'error') {\n statusRef.current = 'error';\n setStatus('error');\n onAuthError?.(msg);\n }\n break;\n }\n default:\n break;\n }\n },\n [handleReadyMessage, handleUpload, handleListAssets, handleDeleteAsset, onChange, onLoad, onSave, onAuthError, setStatusSafely]\n );\n\n useEffect(() => {\n if (typeof window === 'undefined') {\n return;\n }\n window.addEventListener('message', handleMessage);\n return () => {\n window.removeEventListener('message', handleMessage);\n };\n }, [handleMessage]);\n\n useEffect(() => {\n const nextPayload: InitPayload = {\n html: effectiveInitialHtml,\n ...(Array.isArray(mergeTags) ? { mergeTags } : {}),\n ...(typeof mergeTagTrigger === 'string' && mergeTagTrigger.trim()\n ? { mergeTagTrigger: mergeTagTrigger.trim() }\n : {}),\n config: effectiveConfig,\n ...(templateId ? { templateId } : {}),\n showFooter,\n includeUnsubscribe,\n ...(externalFooterHtml !== undefined ? { externalFooterHtml } : {}),\n footerInjectionMode: effectiveFooterMode,\n };\n const signature = stableSignature(nextPayload);\n latestInitRef.current = { type: 'INIT', payload: nextPayload };\n if (signature !== initSignatureRef.current && readyRef.current) {\n postMessage(latestInitRef.current);\n }\n initSignatureRef.current = signature;\n }, [\n effectiveConfig,\n effectiveInitialHtml,\n mergeTags,\n mergeTagTrigger,\n postMessage,\n templateId,\n showFooter,\n includeUnsubscribe,\n externalFooterHtml,\n footerInjectionMode,\n ]);\n\n const handleIframeLoad = useCallback(() => {\n builderWindowRef.current = iframeRef.current?.contentWindow ?? null;\n readyRef.current = false;\n runtimeOriginRef.current = null;\n setStatusSafely('loading');\n\n if (builderWindowRef.current) {\n try {\n builderWindowRef.current.postMessage(buildEnvelope(latestInitRef.current), '*');\n } catch {\n // Ignore and wait for normal READY/message flow.\n }\n }\n\n if (handshakeTimerRef.current) {\n window.clearTimeout(handshakeTimerRef.current);\n }\n if (handshakeRetryIntervalRef.current) {\n window.clearInterval(handshakeRetryIntervalRef.current);\n }\n\n let attempts = 0;\n const maxAttempts = 12;\n handshakeRetryIntervalRef.current = window.setInterval(() => {\n if (readyRef.current) {\n if (handshakeRetryIntervalRef.current) {\n window.clearInterval(handshakeRetryIntervalRef.current);\n handshakeRetryIntervalRef.current = null;\n }\n return;\n }\n attempts += 1;\n try {\n builderWindowRef.current?.postMessage(buildEnvelope(latestInitRef.current), '*');\n } catch {\n // Keep retrying until timeout.\n }\n if (attempts >= maxAttempts) {\n if (handshakeRetryIntervalRef.current) {\n window.clearInterval(handshakeRetryIntervalRef.current);\n handshakeRetryIntervalRef.current = null;\n }\n }\n }, 1000);\n\n handshakeTimerRef.current = window.setTimeout(() => {\n if (readyRef.current) {\n return;\n }\n if (handshakeRetryIntervalRef.current) {\n window.clearInterval(handshakeRetryIntervalRef.current);\n handshakeRetryIntervalRef.current = null;\n }\n setStatusSafely('error');\n onAuthError?.('Builder handshake failed or authentication was rejected.');\n }, 12000);\n }, [onAuthError, setStatusSafely]);\n\n useImperativeHandle(\n ref,\n () => ({\n reload() {\n queueRef.current = [];\n readyRef.current = false;\n builderWindowRef.current = null;\n runtimeOriginRef.current = null;\n if (handshakeTimerRef.current) {\n window.clearTimeout(handshakeTimerRef.current);\n handshakeTimerRef.current = null;\n }\n if (handshakeRetryIntervalRef.current) {\n window.clearInterval(handshakeRetryIntervalRef.current);\n handshakeRetryIntervalRef.current = null;\n }\n setStatusSafely('loading');\n setReloadKey((key) => key + 1);\n },\n }),\n [setStatusSafely]\n );\n\n useEffect(() => {\n return () => {\n if (handshakeTimerRef.current) {\n window.clearTimeout(handshakeTimerRef.current);\n handshakeTimerRef.current = null;\n }\n if (handshakeRetryIntervalRef.current) {\n window.clearInterval(handshakeRetryIntervalRef.current);\n handshakeRetryIntervalRef.current = null;\n }\n };\n }, []);\n\n return (\n <div className={className} style={{ position: 'relative', width: '100%', height: '100%', ...style }}>\n <iframe\n key={reloadKey}\n ref={iframeRef}\n src={iframeSrc}\n title={iframeTitle}\n sandbox={sandbox}\n style={iframeStyle}\n loading=\"lazy\"\n allowFullScreen\n onLoad={handleIframeLoad}\n />\n {!hideLoadingOverlay && status !== 'ready' && (\n <div style={overlayStyle}>Connecting to builder…</div>\n )}\n </div>\n );\n}\n\nexport const EmailBuilder = forwardRef(EmailBuilderInner);\n","export const EMAIL_BUILDER_PROTOCOL_VERSION = '1.0.0';\n\nexport type MessageType =\n | 'INIT'\n | 'READY'\n | 'CHANGE'\n | 'LOADED'\n | 'SAVE'\n | 'UPLOAD'\n | 'UPLOAD_SUCCESS'\n | 'LIST_ASSETS'\n | 'ASSETS_LIST'\n | 'DELETE_ASSET'\n | 'DELETE_ASSET_SUCCESS'\n | 'AUTH_ERROR';\n\nexport interface MessageMeta {\n id: string;\n correlationId?: string;\n version: string;\n sentAt: number;\n}\n\nexport type BuilderConfig = Record<string, unknown> | undefined;\n\nexport interface InitPayload {\n /** Inline HTML to import (optional if templateId is set and builder fetches server-side). */\n html?: string;\n /** Merge tag options used by the embedded editor dropdown. */\n mergeTags?: Array<{ label: string; value: string }>;\n /** Trigger used to open merge tag suggestions (defaults to '@'). */\n mergeTagTrigger?: string;\n /** When set, embedded builder fetches HTML from API using embed token + this id. */\n templateId?: string;\n config?: BuilderConfig;\n /**\n * Optional: host-provided footer HTML to be rendered by the builder (SDK mode).\n * If set, the builder can avoid importing its own footer and render this exact footer instead.\n */\n externalFooterHtml?: string;\n /**\n * Footer source selection.\n * - `default`: builder uses its existing behavior.\n * - `sdk`: builder uses `externalFooterHtml` (and should avoid importing/auto-generating footer).\n */\n footerInjectionMode?: 'default' | 'sdk';\n /** Show \"Powered by Public Circles\" branding in footer. Controlled by host based on add-on purchase. */\n showFooter?: boolean;\n /** Include unsubscribe link in footer. Controlled by host toggle. */\n includeUnsubscribe?: boolean;\n}\n\nexport interface ChangePayload {\n html: string;\n}\n\nexport interface LoadedPayload {\n html: string;\n}\n\nexport interface SavePayload {\n html: string;\n}\n\nexport interface UploadPayload {\n file: File;\n}\n\nexport interface UploadSuccessPayload {\n url: string;\n}\n\nexport interface AssetItem {\n url: string;\n id?: string;\n name?: string;\n thumbnailUrl?: string;\n}\n\nexport interface ListAssetsPayload {\n limit?: number;\n}\n\nexport interface AssetsListPayload {\n assets: AssetItem[];\n}\n\nexport interface DeleteAssetPayload {\n id?: string;\n url?: string;\n}\n\nexport interface DeleteAssetSuccessPayload {\n success: boolean;\n}\n\nexport interface AuthErrorPayload {\n message: string;\n}\n\nexport type Message =\n | { type: 'INIT'; payload: InitPayload; meta?: MessageMeta }\n | { type: 'READY'; meta?: MessageMeta }\n | { type: 'CHANGE'; payload: ChangePayload; meta?: MessageMeta }\n | { type: 'LOADED'; payload: LoadedPayload; meta?: MessageMeta }\n | { type: 'SAVE'; payload: SavePayload; meta?: MessageMeta }\n | { type: 'UPLOAD'; payload: UploadPayload; meta?: MessageMeta }\n | { type: 'UPLOAD_SUCCESS'; payload: UploadSuccessPayload; meta?: MessageMeta }\n | { type: 'LIST_ASSETS'; payload?: ListAssetsPayload; meta?: MessageMeta }\n | { type: 'ASSETS_LIST'; payload: AssetsListPayload; meta?: MessageMeta }\n | { type: 'DELETE_ASSET'; payload: DeleteAssetPayload; meta?: MessageMeta }\n | { type: 'DELETE_ASSET_SUCCESS'; payload: DeleteAssetSuccessPayload; meta?: MessageMeta }\n | { type: 'AUTH_ERROR'; payload: AuthErrorPayload; meta?: MessageMeta };\n\nexport type BuilderToHostMessage = Extract<\n Message,\n { type: 'READY' | 'CHANGE' | 'LOADED' | 'SAVE' | 'UPLOAD' | 'LIST_ASSETS' | 'DELETE_ASSET' | 'AUTH_ERROR' }\n>;\n\nexport type HostToBuilderMessage = Extract<\n Message,\n { type: 'INIT' | 'UPLOAD_SUCCESS' | 'ASSETS_LIST' | 'DELETE_ASSET_SUCCESS' }\n>;\n\nconst VALID_TYPES: MessageType[] = [\n 'INIT',\n 'READY',\n 'CHANGE',\n 'LOADED',\n 'SAVE',\n 'UPLOAD',\n 'UPLOAD_SUCCESS',\n 'LIST_ASSETS',\n 'ASSETS_LIST',\n 'DELETE_ASSET',\n 'DELETE_ASSET_SUCCESS',\n 'AUTH_ERROR',\n];\n\nexport function isMessageLike(value: unknown): value is Message {\n if (typeof value !== 'object' || value === null) {\n return false;\n }\n\n const candidate = value as { type?: unknown; payload?: unknown; meta?: unknown };\n if (typeof candidate.type !== 'string' || !VALID_TYPES.includes(candidate.type as MessageType)) {\n return false;\n }\n\n if ('meta' in candidate && candidate.meta !== undefined) {\n const meta = candidate.meta as Partial<MessageMeta>;\n if (\n (meta.id && typeof meta.id !== 'string') ||\n (meta.correlationId && typeof meta.correlationId !== 'string') ||\n (meta.version && typeof meta.version !== 'string') ||\n (meta.sentAt && typeof meta.sentAt !== 'number')\n ) {\n return false;\n }\n }\n\n return true;\n}\n\nexport function createMessageMeta(correlationId?: string): MessageMeta {\n const id = typeof crypto !== 'undefined' && 'randomUUID' in crypto\n ? crypto.randomUUID()\n : Math.random().toString(36).slice(2);\n return {\n id,\n correlationId,\n version: EMAIL_BUILDER_PROTOCOL_VERSION,\n sentAt: Date.now(),\n };\n}\n","import type { BuilderToHostMessage, HostToBuilderMessage, MessageMeta } from './protocol';\nimport { createMessageMeta, isMessageLike } from './protocol';\n\nexport function deriveAllowedOrigin(src: string, override?: string): string {\n if (override) {\n const parsed = new URL(override);\n if (parsed.origin === 'null') {\n throw new Error('allowedOrigin must resolve to a valid origin');\n }\n return parsed.origin;\n }\n\n let parsed: URL;\n try {\n parsed = new URL(src);\n } catch {\n throw new Error('EmailBuilder `src` must be an absolute URL (e.g. https://example.com)');\n }\n if (!parsed.origin || parsed.origin === 'null') {\n throw new Error('EmailBuilder requires an absolute src URL with a valid origin');\n }\n return parsed.origin;\n}\n\nexport function buildEnvelope<T extends HostToBuilderMessage>(message: T, correlationId?: string): T {\n const meta: MessageMeta = createMessageMeta(correlationId);\n return { ...message, meta } as T;\n}\n\nexport function sanitizeIncomingMessage(\n event: MessageEvent,\n allowedOrigin: string,\n iframeWindow: Window | null\n): BuilderToHostMessage | null {\n if (event.origin !== allowedOrigin) {\n return null;\n }\n\n if (!iframeWindow || event.source !== iframeWindow) {\n return null;\n }\n\n if (!isMessageLike(event.data)) {\n return null;\n }\n\n return event.data as BuilderToHostMessage;\n}\n\nexport function stableSignature(value: unknown): string {\n return JSON.stringify(value ?? null);\n}\n"],"mappings":"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","names":["forwardRef","useCallback","useEffect","useImperativeHandle","useMemo","useRef","useState","EMAIL_BUILDER_PROTOCOL_VERSION","VALID_TYPES","isMessageLike","value","candidate","meta","createMessageMeta","correlationId","deriveAllowedOrigin","src","override","parsed","buildEnvelope","message","correlationId","meta","createMessageMeta","stableSignature","value","jsx","jsxs","DEFAULT_SANDBOX","DEFAULT_INITIAL_HTML","DEFAULT_BUILDER_SRC","overlayStyle","iframeStyle","appendPreviewParamsToSrc","src","preview","u","sep","appendEmbedTokenToSrc","embedToken","EmailBuilderInner","initialHtml","mergeTags","mergeTagTrigger","templateId","config","showFooter","includeUnsubscribe","externalFooterHtml","footerInjectionMode","previewOnly","className","style","hideLoadingOverlay","iframeTitle","sandbox","onChange","onLoad","onSave","onUpload","onListAssets","onDeleteAsset","onReady","onStatusChange","onAuthError","allowedOrigin","ref","resolvedSrc","isPreviewMode","effectiveConfig","useMemo","iframeSrc","iframeRef","useRef","builderWindowRef","readyRef","statusRef","status","setStatus","useState","reloadKey","setReloadKey","queueRef","runtimeOriginRef","handshakeTimerRef","handshakeRetryIntervalRef","effectiveInitialHtml","effectiveFooterMode","initPayload","initSignatureRef","stableSignature","latestInitRef","expectedOrigin","deriveAllowedOrigin","resolveTargetOrigin","useCallback","setStatusSafely","next","postMessage","message","correlationId","_a","_b","target","buildEnvelope","flushQueue","pending","handleReadyMessage","handleUpload","eventMessage","url","error","handleListAssets","_c","assets","handleDeleteAsset","success","handleMessage","event","iframeWindow","isMessageLike","msg","useEffect","nextPayload","signature","handleIframeLoad","attempts","maxAttempts","useImperativeHandle","key","EmailBuilder","forwardRef"]}
|