@alihaiderrana/email-builder-sdk 0.1.14 → 0.1.15
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 +2 -0
- package/dist/index.d.ts +2 -0
- 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 Y=Object.defineProperty;var
|
|
1
|
+
"use strict";var Y=Object.defineProperty;var we=Object.getOwnPropertyDescriptor;var Ae=Object.getOwnPropertyNames;var Me=Object.prototype.hasOwnProperty;var Le=(r,n)=>{for(var a in n)Y(r,a,{get:n[a],enumerable:!0})},Ie=(r,n,a,p)=>{if(n&&typeof n=="object"||typeof n=="function")for(let S of Ae(n))!Me.call(r,S)&&S!==a&&Y(r,S,{get:()=>n[S],enumerable:!(p=we(n,S))||p.enumerable});return r};var De=r=>Ie(Y({},"__esModule",{value:!0}),r);var He={};Le(He,{EMAIL_BUILDER_PROTOCOL_VERSION:()=>ne,EmailBuilder:()=>xe,createMessageMeta:()=>G,isMessageLike:()=>C});module.exports=De(He);var t=require("react");var ne="1.0.0",Pe=["INIT","READY","CHANGE","LOADED","SAVE","UPLOAD","UPLOAD_SUCCESS","LIST_ASSETS","ASSETS_LIST","DELETE_ASSET","DELETE_ASSET_SUCCESS","AUTH_ERROR"];function C(r){if(typeof r!="object"||r===null)return!1;let n=r;if(typeof n.type!="string"||!Pe.includes(n.type))return!1;if("meta"in n&&n.meta!==void 0){let a=n.meta;if(a.id&&typeof a.id!="string"||a.correlationId&&typeof a.correlationId!="string"||a.version&&typeof a.version!="string"||a.sentAt&&typeof a.sentAt!="number")return!1}return!0}function G(r){return{id:typeof crypto!="undefined"&&"randomUUID"in crypto?crypto.randomUUID():Math.random().toString(36).slice(2),correlationId:r,version:ne,sentAt:Date.now()}}function se(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 a;try{a=new URL(r)}catch{throw new Error("EmailBuilder `src` must be an absolute URL (e.g. https://example.com)")}if(!a.origin||a.origin==="null")throw new Error("EmailBuilder requires an absolute src URL with a valid origin");return a.origin}function A(r,n){let a=G(n);return{...r,meta:a}}function $(r){return JSON.stringify(r!=null?r:null)}var M=require("react/jsx-runtime"),Ue="allow-scripts allow-same-origin allow-forms",_e="<h1>Hello World</h1><p>Start building your email template.</p>",Re="https://pc-email-template-builder.netlify.app",he={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 be(r,n){if(!n)return r;try{let a=new URL(r,typeof window!="undefined"?window.location.href:"https://example.com");return a.searchParams.set("preview","true"),a.searchParams.set("previewOnly","true"),a.toString()}catch{let a=r.includes("?")?"&":"?";return`${r}${a}preview=true&previewOnly=true`}}function Oe(r,n){if(!n||!n.trim())return r;try{let a=new URL(r,typeof window!="undefined"?window.location.href:"https://example.com");return a.searchParams.set("embedToken",n),a.toString()}catch{let a=r.includes("?")?"&":"?";return`${r}${a}embedToken=${encodeURIComponent(n)}`}}function Ce({src:r,initialHtml:n,embedToken:a,templateId:p,config:S,showFooter:x,includeUnsubscribe:H,externalFooterHtml:m,footerInjectionMode:j,preview:ie=!1,previewOnly:oe=!1,className:le,style:ue,hideLoadingOverlay:ce=!1,iframeTitle:de="Email Builder",sandbox:pe=Ue,onChange:L,onLoad:I,onSave:D,onUpload:k,onListAssets:v,onDeleteAsset:N,onReady:P,onStatusChange:U,onAuthError:E,allowedOrigin:z},fe){let _=(r==null?void 0:r.trim())||Re,R=ie||oe,W=(0,t.useMemo)(()=>R?{...S!=null?S:{},preview:!0,previewOnly:!0}:S,[S,R]),Se=(0,t.useMemo)(()=>Oe(be(_,R),a),[_,a,R]),h=(0,t.useRef)(null),d=(0,t.useRef)(null),y=(0,t.useRef)(!1),B=(0,t.useRef)("loading"),[ye,q]=(0,t.useState)("loading"),[Ee,ge]=(0,t.useState)(0),b=(0,t.useRef)([]),T=(0,t.useRef)(null),f=(0,t.useRef)(null),i=(0,t.useRef)(null),V=typeof n=="string"?n:p?"":_e,F=j||(m&&m.trim()?"sdk":"default"),J={html:V,config:W,...p?{templateId:p}:{},showFooter:x,includeUnsubscribe:H,...m?{externalFooterHtml:m}:{},footerInjectionMode:F},Q=(0,t.useRef)($(J)),w=(0,t.useRef)({type:"INIT",payload:J}),ke=(0,t.useMemo)(()=>se(_,z),[_,z]),O=(0,t.useCallback)(()=>"*",[]),g=(0,t.useCallback)(e=>{B.current!==e&&(B.current=e,q(e),U==null||U(e))},[U]),c=(0,t.useCallback)((e,o)=>{var l,u;let s=(u=(l=h.current)==null?void 0:l.contentWindow)!=null?u:d.current;if(s&&(d.current=s),!d.current||!y.current){b.current.push({message:e,correlationId:o});return}d.current.postMessage(A(e,o),O())},[O]),X=(0,t.useCallback)(()=>{if(!y.current||!d.current)return;let e=[...b.current];b.current=[],e.forEach(({message:o,correlationId:s})=>{var l;(l=d.current)==null||l.postMessage(A(o,s),O())})},[O]),Z=(0,t.useCallback)(()=>{y.current||(f.current&&(window.clearTimeout(f.current),f.current=null),i.current&&(window.clearInterval(i.current),i.current=null),y.current=!0,g("ready"),P==null||P(),c(w.current),X())},[X,P,c,g]),ee=(0,t.useCallback)(async e=>{var o,s;if(e.type==="UPLOAD"){if(!k){console.warn("[EmailBuilderSDK] Upload requested but no onUpload handler is configured.");return}try{let l=await k(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)}}},[k,c]),te=(0,t.useCallback)(async e=>{var o,s,l;if(e.type==="LIST_ASSETS"){if(!v){c({type:"ASSETS_LIST",payload:{assets:[]}},(o=e.meta)==null?void 0:o.id);return}try{let u=await v(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)}}},[v,c]),re=(0,t.useCallback)(async e=>{var o,s,l;if(e.type==="DELETE_ASSET"){if(!N){c({type:"DELETE_ASSET_SUCCESS",payload:{success:!1}},(o=e.meta)==null?void 0:o.id);return}try{let u=await N(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)}}},[N,c]),K=(0,t.useCallback)(e=>{var l,u,ae;let o=(u=(l=h.current)==null?void 0:l.contentWindow)!=null?u:d.current;if(!o||e.source!==o||!C(e.data))return;if(!T.current)T.current=e.origin;else if(e.origin!==T.current)return;let s=e.data;switch(e.source&&e.source!==d.current&&(d.current=e.source),s.type){case"READY":Z();break;case"CHANGE":L==null||L(s.payload.html);break;case"LOADED":I==null||I(s.payload.html);break;case"SAVE":D==null||D(s.payload.html);break;case"UPLOAD":ee(s);break;case"LIST_ASSETS":te(s);break;case"DELETE_ASSET":re(s);break;case"AUTH_ERROR":{let Te=((ae=s.payload)==null?void 0:ae.message)||"Email builder authentication failed";B.current!=="error"&&(B.current="error",q("error"),E==null||E(Te));break}default:break}},[Z,ee,te,re,L,I,D,E,g]);(0,t.useEffect)(()=>{if(typeof window!="undefined")return window.addEventListener("message",K),()=>{window.removeEventListener("message",K)}},[K]),(0,t.useEffect)(()=>{let e={html:V,config:W,...p?{templateId:p}:{},showFooter:x,includeUnsubscribe:H,...m!==void 0?{externalFooterHtml:m}:{},footerInjectionMode:F},o=$(e);w.current={type:"INIT",payload:e},o!==Q.current&&y.current&&c(w.current),Q.current=o},[W,V,c,p,x,H,m,j]);let me=(0,t.useCallback)(()=>{var s,l;if(d.current=(l=(s=h.current)==null?void 0:s.contentWindow)!=null?l:null,y.current=!1,T.current=null,g("loading"),d.current)try{d.current.postMessage(A(w.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(y.current){i.current&&(window.clearInterval(i.current),i.current=null);return}e+=1;try{(u=d.current)==null||u.postMessage(A(w.current),"*")}catch{}e>=o&&i.current&&(window.clearInterval(i.current),i.current=null)},1e3),f.current=window.setTimeout(()=>{y.current||(i.current&&(window.clearInterval(i.current),i.current=null),g("error"),E==null||E("Builder handshake failed or authentication was rejected."))},12e3)},[E,g]);return(0,t.useImperativeHandle)(fe,()=>({reload(){b.current=[],y.current=!1,d.current=null,T.current=null,f.current&&(window.clearTimeout(f.current),f.current=null),i.current&&(window.clearInterval(i.current),i.current=null),g("loading"),ge(e=>e+1)}}),[g]),(0,t.useEffect)(()=>()=>{f.current&&(window.clearTimeout(f.current),f.current=null),i.current&&(window.clearInterval(i.current),i.current=null)},[]),(0,M.jsxs)("div",{className:le,style:{position:"relative",width:"100%",height:"100%",...ue},children:[(0,M.jsx)("iframe",{ref:h,src:Se,title:de,sandbox:pe,style:Be,loading:"lazy",allowFullScreen:!0,onLoad:me},Ee),!ce&&ye!=="ready"&&(0,M.jsx)("div",{style:he,children:"Connecting to builder\u2026"})]})}var xe=(0,t.forwardRef)(Ce);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":"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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","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 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 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 {!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 /** 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","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
|
@@ -167,6 +167,8 @@ interface EmailBuilderProps {
|
|
|
167
167
|
previewOnly?: boolean;
|
|
168
168
|
className?: string;
|
|
169
169
|
style?: CSSProperties;
|
|
170
|
+
/** Hide the SDK's default iframe loading overlay so the host can render its own loader. */
|
|
171
|
+
hideLoadingOverlay?: boolean;
|
|
170
172
|
iframeTitle?: string;
|
|
171
173
|
sandbox?: string;
|
|
172
174
|
onChange?: (html: string) => void;
|
package/dist/index.d.ts
CHANGED
|
@@ -167,6 +167,8 @@ interface EmailBuilderProps {
|
|
|
167
167
|
previewOnly?: boolean;
|
|
168
168
|
className?: string;
|
|
169
169
|
style?: CSSProperties;
|
|
170
|
+
/** Hide the SDK's default iframe loading overlay so the host can render its own loader. */
|
|
171
|
+
hideLoadingOverlay?: boolean;
|
|
170
172
|
iframeTitle?: string;
|
|
171
173
|
sandbox?: string;
|
|
172
174
|
onChange?: (html: string) => void;
|
package/dist/index.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import{forwardRef as
|
|
1
|
+
import{forwardRef as Ie,useCallback as p,useEffect as G,useImperativeHandle as De,useMemo as $,useRef as f,useState as ie}from"react";var Me="1.0.0",Le=["INIT","READY","CHANGE","LOADED","SAVE","UPLOAD","UPLOAD_SUCCESS","LIST_ASSETS","ASSETS_LIST","DELETE_ASSET","DELETE_ASSET_SUCCESS","AUTH_ERROR"];function K(r){if(typeof r!="object"||r===null)return!1;let n=r;if(typeof n.type!="string"||!Le.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 ne(r){return{id:typeof crypto!="undefined"&&"randomUUID"in crypto?crypto.randomUUID():Math.random().toString(36).slice(2),correlationId:r,version:Me,sentAt:Date.now()}}function se(r,n){if(n){let S=new URL(n);if(S.origin==="null")throw new Error("allowedOrigin must resolve to a valid origin");return S.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 M(r,n){let t=ne(n);return{...r,meta:t}}function Y(r){return JSON.stringify(r!=null?r:null)}import{jsx as oe,jsxs as Ce}from"react/jsx-runtime";var Pe="allow-scripts allow-same-origin allow-forms",Ue="<h1>Hello World</h1><p>Start building your email template.</p>",_e="https://pc-email-template-builder.netlify.app",Re={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},he={border:"none",width:"100%",height:"100%"};function Be(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 be(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 Oe({src:r,initialHtml:n,embedToken:t,templateId:S,config:T,showFooter:C,includeUnsubscribe:x,externalFooterHtml:m,footerInjectionMode:j,preview:le=!1,previewOnly:ue=!1,className:ce,style:de,hideLoadingOverlay:pe=!1,iframeTitle:fe="Email Builder",sandbox:Se=Pe,onChange:L,onLoad:I,onSave:D,onUpload:H,onListAssets:k,onDeleteAsset:v,onReady:P,onStatusChange:U,onAuthError:E,allowedOrigin:z},ye){let _=(r==null?void 0:r.trim())||_e,R=le||ue,N=$(()=>R?{...T!=null?T:{},preview:!0,previewOnly:!0}:T,[T,R]),Ee=$(()=>be(Be(_,R),t),[_,t,R]),h=f(null),c=f(null),y=f(!1),B=f("loading"),[ge,q]=ie("loading"),[me,Te]=ie(0),b=f([]),w=f(null),d=f(null),s=f(null),W=typeof n=="string"?n:S?"":Ue,F=j||(m&&m.trim()?"sdk":"default"),J={html:W,config:N,...S?{templateId:S}:{},showFooter:C,includeUnsubscribe:x,...m?{externalFooterHtml:m}:{},footerInjectionMode:F},Q=f(Y(J)),A=f({type:"INIT",payload:J}),xe=$(()=>se(_,z),[_,z]),O=p(()=>"*",[]),g=p(e=>{B.current!==e&&(B.current=e,q(e),U==null||U(e))},[U]),u=p((e,i)=>{var o,l;let a=(l=(o=h.current)==null?void 0:o.contentWindow)!=null?l:c.current;if(a&&(c.current=a),!c.current||!y.current){b.current.push({message:e,correlationId:i});return}c.current.postMessage(M(e,i),O())},[O]),X=p(()=>{if(!y.current||!c.current)return;let e=[...b.current];b.current=[],e.forEach(({message:i,correlationId:a})=>{var o;(o=c.current)==null||o.postMessage(M(i,a),O())})},[O]),Z=p(()=>{y.current||(d.current&&(window.clearTimeout(d.current),d.current=null),s.current&&(window.clearInterval(s.current),s.current=null),y.current=!0,g("ready"),P==null||P(),u(A.current),X())},[X,P,u,g]),ee=p(async e=>{var i,a;if(e.type==="UPLOAD"){if(!H){console.warn("[EmailBuilderSDK] Upload requested but no onUpload handler is configured.");return}try{let o=await H(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)}}},[H,u]),te=p(async e=>{var i,a,o;if(e.type==="LIST_ASSETS"){if(!k){u({type:"ASSETS_LIST",payload:{assets:[]}},(i=e.meta)==null?void 0:i.id);return}try{let l=await k(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)}}},[k,u]),re=p(async e=>{var i,a,o;if(e.type==="DELETE_ASSET"){if(!v){u({type:"DELETE_ASSET_SUCCESS",payload:{success:!1}},(i=e.meta)==null?void 0:i.id);return}try{let l=await v(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)}}},[v,u]),V=p(e=>{var o,l,ae;let i=(l=(o=h.current)==null?void 0:o.contentWindow)!=null?l:c.current;if(!i||e.source!==i||!K(e.data))return;if(!w.current)w.current=e.origin;else if(e.origin!==w.current)return;let a=e.data;switch(e.source&&e.source!==c.current&&(c.current=e.source),a.type){case"READY":Z();break;case"CHANGE":L==null||L(a.payload.html);break;case"LOADED":I==null||I(a.payload.html);break;case"SAVE":D==null||D(a.payload.html);break;case"UPLOAD":ee(a);break;case"LIST_ASSETS":te(a);break;case"DELETE_ASSET":re(a);break;case"AUTH_ERROR":{let Ae=((ae=a.payload)==null?void 0:ae.message)||"Email builder authentication failed";B.current!=="error"&&(B.current="error",q("error"),E==null||E(Ae));break}default:break}},[Z,ee,te,re,L,I,D,E,g]);G(()=>{if(typeof window!="undefined")return window.addEventListener("message",V),()=>{window.removeEventListener("message",V)}},[V]),G(()=>{let e={html:W,config:N,...S?{templateId:S}:{},showFooter:C,includeUnsubscribe:x,...m!==void 0?{externalFooterHtml:m}:{},footerInjectionMode:F},i=Y(e);A.current={type:"INIT",payload:e},i!==Q.current&&y.current&&u(A.current),Q.current=i},[N,W,u,S,C,x,m,j]);let we=p(()=>{var a,o;if(c.current=(o=(a=h.current)==null?void 0:a.contentWindow)!=null?o:null,y.current=!1,w.current=null,g("loading"),c.current)try{c.current.postMessage(M(A.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(M(A.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),g("error"),E==null||E("Builder handshake failed or authentication was rejected."))},12e3)},[E,g]);return De(ye,()=>({reload(){b.current=[],y.current=!1,c.current=null,w.current=null,d.current&&(window.clearTimeout(d.current),d.current=null),s.current&&(window.clearInterval(s.current),s.current=null),g("loading"),Te(e=>e+1)}}),[g]),G(()=>()=>{d.current&&(window.clearTimeout(d.current),d.current=null),s.current&&(window.clearInterval(s.current),s.current=null)},[]),Ce("div",{className:ce,style:{position:"relative",width:"100%",height:"100%",...de},children:[oe("iframe",{ref:h,src:Ee,title:fe,sandbox:Se,style:he,loading:"lazy",allowFullScreen:!0,onLoad:we},me),!pe&&ge!=="ready"&&oe("div",{style:Re,children:"Connecting to builder\u2026"})]})}var $e=Ie(Oe);export{Me as EMAIL_BUILDER_PROTOCOL_VERSION,$e as EmailBuilder,ne as createMessageMeta,K 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 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 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 {!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 /** 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","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"]}
|