@alihaiderrana/email-builder-sdk 0.1.11 → 0.1.13

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -21,6 +21,8 @@ export function EmailEditor() {
21
21
  initialHtml="<h1>Welcome</h1><p>Edit this email template</p>"
22
22
  showFooter={true}
23
23
  includeUnsubscribe={true}
24
+ footerInjectionMode="sdk"
25
+ externalFooterHtml="<div>Custom footer</div>"
24
26
  onAuthError={(message) => console.error('auth failed', message)}
25
27
  onChange={(html) => console.log('changed', html)}
26
28
  onSave={(html) => console.log('saved', html)}
@@ -31,6 +33,16 @@ export function EmailEditor() {
31
33
  const data = await response.json();
32
34
  return data.url;
33
35
  }}
36
+ onListAssets={async () => {
37
+ const response = await fetch('/api/assets');
38
+ const data = await response.json();
39
+ return data.assets;
40
+ }}
41
+ onDeleteAsset={async ({ id }) => {
42
+ if (!id) return false;
43
+ const response = await fetch(`/api/assets/${id}`, { method: 'DELETE' });
44
+ return response.ok;
45
+ }}
34
46
  />
35
47
  </div>
36
48
  );
@@ -47,8 +59,11 @@ If `src` is not provided, the SDK uses the managed Circles builder endpoint.
47
59
  | `embedToken` | `string` | Yes | Token passed to iframe for backend verification. |
48
60
  | `src` | `string` | No | Optional custom builder URL override. |
49
61
  | `initialHtml` | `string` | No | Initial HTML content to load in the editor. |
62
+ | `templateId` | `string` | No | Optional template id; builder fetches HTML from backend when provided. |
50
63
  | `showFooter` | `boolean` | No | Show "Powered by Public Circles" branding in footer. Defaults to `true`. |
51
64
  | `includeUnsubscribe` | `boolean` | No | Include unsubscribe link in footer. Defaults to `true`. |
65
+ | `externalFooterHtml` | `string` | No | Optional host-provided footer HTML snippet. |
66
+ | `footerInjectionMode` | `'default' \| 'sdk'` | No | Footer source mode. Use `'sdk'` to apply `externalFooterHtml`. |
52
67
  | `config` | `Record<string, unknown>` | No | Optional builder configuration object. |
53
68
  | `className` | `string` | No | Class for the wrapper container. |
54
69
  | `style` | `React.CSSProperties` | No | Inline styles for the wrapper container. |
@@ -60,8 +75,31 @@ If `src` is not provided, the SDK uses the managed Circles builder endpoint.
60
75
  | `onChange` | `(html: string) => void` | No | Called when editor content changes. |
61
76
  | `onSave` | `(html: string) => void` | No | Called when user saves content. |
62
77
  | `onUpload` | `(file: File) => Promise<string>` | No | Upload handler that returns a public URL for inserted assets. |
78
+ | `onListAssets` | `(payload?: { limit?: number }) => Promise<AssetItem[]>` | No | Returns previously uploaded images for the picker modal. |
79
+ | `onDeleteAsset` | `(payload: { id?: string; url?: string }) => Promise<boolean>` | No | Deletes an uploaded asset and returns whether it succeeded. |
63
80
  | `onAuthError` | `(message: string) => void` | No | Called when token is missing/invalid/rejected. |
64
81
 
82
+ `AssetItem` shape:
83
+
84
+ ```ts
85
+ type AssetItem = {
86
+ url: string;
87
+ id?: string;
88
+ name?: string;
89
+ thumbnailUrl?: string;
90
+ };
91
+ ```
92
+
93
+ ## Image Management Contract
94
+
95
+ To support reusable image uploads for any client app:
96
+
97
+ - implement `onUpload` to upload a file and return a public URL
98
+ - implement `onListAssets` to return uploaded images
99
+ - implement `onDeleteAsset` to delete uploaded images from your backend/storage
100
+
101
+ The SDK stays storage-agnostic (S3, GCS, Cloudflare R2, etc.).
102
+
65
103
  ## Ref API
66
104
 
67
105
  `EmailBuilder` supports a ref with:
package/dist/index.cjs CHANGED
@@ -1,2 +1,2 @@
1
- "use strict";var v=Object.defineProperty;var ce=Object.getOwnPropertyDescriptor;var de=Object.getOwnPropertyNames;var fe=Object.prototype.hasOwnProperty;var pe=(r,n)=>{for(var i in n)v(r,i,{get:n[i],enumerable:!0})},ge=(r,n,i,u)=>{if(n&&typeof n=="object"||typeof n=="function")for(let p of de(n))!fe.call(r,p)&&p!==i&&v(r,p,{get:()=>n[p],enumerable:!(u=ce(n,p))||u.enumerable});return r};var ye=r=>ge(v({},"__esModule",{value:!0}),r);var Pe={};pe(Pe,{EMAIL_BUILDER_PROTOCOL_VERSION:()=>Q,EmailBuilder:()=>Ue,createMessageMeta:()=>N,isMessageLike:()=>O});module.exports=ye(Pe);var e=require("react");var Q="1.0.0",me=["INIT","READY","CHANGE","SAVE","UPLOAD","UPLOAD_SUCCESS","AUTH_ERROR"];function O(r){if(typeof r!="object"||r===null)return!1;let n=r;if(typeof n.type!="string"||!me.includes(n.type))return!1;if("meta"in n&&n.meta!==void 0){let i=n.meta;if(i.id&&typeof i.id!="string"||i.correlationId&&typeof i.correlationId!="string"||i.version&&typeof i.version!="string"||i.sentAt&&typeof i.sentAt!="number")return!1}return!0}function N(r){return{id:typeof crypto!="undefined"&&"randomUUID"in crypto?crypto.randomUUID():Math.random().toString(36).slice(2),correlationId:r,version:Q,sentAt:Date.now()}}function X(r,n){if(n){let u=new URL(n);if(u.origin==="null")throw new Error("allowedOrigin must resolve to a valid origin");return u.origin}let i;try{i=new URL(r)}catch{throw new Error("EmailBuilder `src` must be an absolute URL (e.g. https://example.com)")}if(!i.origin||i.origin==="null")throw new Error("EmailBuilder requires an absolute src URL with a valid origin");return i.origin}function T(r,n){let i=N(n);return{...r,meta:i}}function W(r){return JSON.stringify(r!=null?r:null)}var R=require("react/jsx-runtime"),we="allow-scripts allow-same-origin allow-forms",Me="<h1>Hello World</h1><p>Start building your email template.</p>",Ee="https://pc-email-template-builder.netlify.app",Se={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},Ie={border:"none",width:"100%",height:"100%"};function Te(r,n){if(!n||!n.trim())return r;try{let i=new URL(r,typeof window!="undefined"?window.location.href:"https://example.com");return i.searchParams.set("embedToken",n),i.toString()}catch{let i=r.includes("?")?"&":"?";return`${r}${i}embedToken=${encodeURIComponent(n)}`}}function Re({src:r,initialHtml:n,embedToken:i,templateId:u,config:p,showFooter:x,includeUnsubscribe:H,externalFooterHtml:w,footerInjectionMode:V,className:Z,style:ee,iframeTitle:re="Email Builder",sandbox:te=we,onChange:U,onSave:P,onUpload:k,onReady:A,onStatusChange:B,onAuthError:y,allowedOrigin:Y},ne){let L=(r==null?void 0:r.trim())||Ee,ie=(0,e.useMemo)(()=>Te(L,i),[L,i]),h=(0,e.useRef)(null),l=(0,e.useRef)(null),g=(0,e.useRef)(!1),E=(0,e.useRef)("loading"),[ae,G]=(0,e.useState)("loading"),[se,oe]=(0,e.useState)(0),b=(0,e.useRef)([]),S=(0,e.useRef)(null),c=(0,e.useRef)(null),a=(0,e.useRef)(null),_=typeof n=="string"?n:u?"":Me,j=V||(w&&w.trim()?"sdk":"default"),z={html:_,config:p,...u?{templateId:u}:{},showFooter:x,includeUnsubscribe:H,...w?{externalFooterHtml:w}:{},footerInjectionMode:j},K=(0,e.useRef)(W(z)),I=(0,e.useRef)({type:"INIT",payload:z}),Ae=(0,e.useMemo)(()=>X(L,Y),[L,Y]),D=(0,e.useCallback)(()=>"*",[]),d=(0,e.useCallback)(t=>{E.current!==t&&(E.current=t,G(t),B==null||B(t))},[B]),M=(0,e.useCallback)((t,o)=>{var f,m;let s=(m=(f=h.current)==null?void 0:f.contentWindow)!=null?m:l.current;if(s&&(l.current=s),!l.current||!g.current){b.current.push({message:t,correlationId:o});return}l.current.postMessage(T(t,o),D())},[D]),q=(0,e.useCallback)(()=>{if(!g.current||!l.current)return;let t=[...b.current];b.current=[],t.forEach(({message:o,correlationId:s})=>{var f;(f=l.current)==null||f.postMessage(T(o,s),D())})},[D]),F=(0,e.useCallback)(()=>{g.current||(c.current&&(window.clearTimeout(c.current),c.current=null),a.current&&(window.clearInterval(a.current),a.current=null),g.current=!0,d("ready"),A==null||A(),M(I.current),q())},[q,A,M,d]),$=(0,e.useCallback)(async t=>{var o;if(t.type==="UPLOAD"){if(!k){console.warn("[EmailBuilderSDK] Upload requested but no onUpload handler is configured.");return}try{d("loading");let s=await k(t.payload.file);M({type:"UPLOAD_SUCCESS",payload:{url:s}},(o=t.meta)==null?void 0:o.id)}catch(s){d("error"),console.error("[EmailBuilderSDK] Upload handler failed",s)}finally{E.current!=="error"&&d("ready")}}},[k,M,d]),C=(0,e.useCallback)(t=>{var f,m,J;let o=(m=(f=h.current)==null?void 0:f.contentWindow)!=null?m:l.current;if(!o||t.source!==o||!O(t.data))return;if(!S.current)S.current=t.origin;else if(t.origin!==S.current)return;let s=t.data;switch(t.source&&t.source!==l.current&&(l.current=t.source),s.type){case"READY":F();break;case"CHANGE":U==null||U(s.payload.html);break;case"SAVE":P==null||P(s.payload.html);break;case"UPLOAD":$(s);break;case"AUTH_ERROR":{let ue=((J=s.payload)==null?void 0:J.message)||"Email builder authentication failed";E.current!=="error"&&(E.current="error",G("error"),y==null||y(ue));break}default:break}},[F,$,U,P,y,d]);(0,e.useEffect)(()=>{if(typeof window!="undefined")return window.addEventListener("message",C),()=>{window.removeEventListener("message",C)}},[C]),(0,e.useEffect)(()=>{let t={html:_,config:p,...u?{templateId:u}:{},showFooter:x,includeUnsubscribe:H,...w!==void 0?{externalFooterHtml:w}:{},footerInjectionMode:j},o=W(t);I.current={type:"INIT",payload:t},o!==K.current&&g.current&&M(I.current),K.current=o},[p,_,M,u,x,H,w,V]);let le=(0,e.useCallback)(()=>{var s,f;if(l.current=(f=(s=h.current)==null?void 0:s.contentWindow)!=null?f:null,g.current=!1,S.current=null,d("loading"),l.current)try{l.current.postMessage(T(I.current),"*")}catch{}c.current&&window.clearTimeout(c.current),a.current&&window.clearInterval(a.current);let t=0,o=12;a.current=window.setInterval(()=>{var m;if(g.current){a.current&&(window.clearInterval(a.current),a.current=null);return}t+=1;try{(m=l.current)==null||m.postMessage(T(I.current),"*")}catch{}t>=o&&a.current&&(window.clearInterval(a.current),a.current=null)},1e3),c.current=window.setTimeout(()=>{g.current||(a.current&&(window.clearInterval(a.current),a.current=null),d("error"),y==null||y("Builder handshake failed or authentication was rejected."))},12e3)},[y,d]);return(0,e.useImperativeHandle)(ne,()=>({reload(){b.current=[],g.current=!1,l.current=null,S.current=null,c.current&&(window.clearTimeout(c.current),c.current=null),a.current&&(window.clearInterval(a.current),a.current=null),d("loading"),oe(t=>t+1)}}),[d]),(0,e.useEffect)(()=>()=>{c.current&&(window.clearTimeout(c.current),c.current=null),a.current&&(window.clearInterval(a.current),a.current=null)},[]),(0,R.jsxs)("div",{className:Z,style:{position:"relative",width:"100%",height:"100%",...ee},children:[(0,R.jsx)("iframe",{ref:h,src:ie,title:re,sandbox:te,style:Ie,loading:"lazy",allowFullScreen:!0,onLoad:le},se),ae!=="ready"&&(0,R.jsx)("div",{style:Se,children:"Connecting to builder\u2026"})]})}var Ue=(0,e.forwardRef)(Re);0&&(module.exports={EMAIL_BUILDER_PROTOCOL_VERSION,EmailBuilder,createMessageMeta,isMessageLike});
1
+ "use strict";var K=Object.defineProperty;var me=Object.getOwnPropertyDescriptor;var Te=Object.getOwnPropertyNames;var we=Object.prototype.hasOwnProperty;var Ae=(r,n)=>{for(var a in n)K(r,a,{get:n[a],enumerable:!0})},Me=(r,n,a,p)=>{if(n&&typeof n=="object"||typeof n=="function")for(let S of Te(n))!we.call(r,S)&&S!==a&&K(r,S,{get:()=>n[S],enumerable:!(p=me(n,S))||p.enumerable});return r};var Le=r=>Me(K({},"__esModule",{value:!0}),r);var xe={};Ae(xe,{EMAIL_BUILDER_PROTOCOL_VERSION:()=>ae,EmailBuilder:()=>Ce,createMessageMeta:()=>Y,isMessageLike:()=>C});module.exports=Le(xe);var t=require("react");var ae="1.0.0",Ie=["INIT","READY","CHANGE","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"||!Ie.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 Y(r){return{id:typeof crypto!="undefined"&&"randomUUID"in crypto?crypto.randomUUID():Math.random().toString(36).slice(2),correlationId:r,version:ae,sentAt:Date.now()}}function ne(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=Y(n);return{...r,meta:a}}function G(r){return JSON.stringify(r!=null?r:null)}var M=require("react/jsx-runtime"),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",De={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},Re={border:"none",width:"100%",height:"100%"};function he(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 Be(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 be({src:r,initialHtml:n,embedToken:a,templateId:p,config:S,showFooter:x,includeUnsubscribe:O,externalFooterHtml:m,footerInjectionMode:$,preview:se=!1,previewOnly:ie=!1,className:oe,style:le,iframeTitle:ue="Email Builder",sandbox:ce=Pe,onChange:L,onSave:I,onUpload:H,onListAssets:k,onDeleteAsset:v,onReady:P,onStatusChange:U,onAuthError:E,allowedOrigin:j},de){let _=(r==null?void 0:r.trim())||_e,D=se||ie,N=(0,t.useMemo)(()=>D?{...S!=null?S:{},preview:!0,previewOnly:!0}:S,[S,D]),pe=(0,t.useMemo)(()=>Be(he(_,D),a),[_,a,D]),R=(0,t.useRef)(null),d=(0,t.useRef)(null),y=(0,t.useRef)(!1),h=(0,t.useRef)("loading"),[fe,z]=(0,t.useState)("loading"),[Se,ye]=(0,t.useState)(0),B=(0,t.useRef)([]),T=(0,t.useRef)(null),f=(0,t.useRef)(null),i=(0,t.useRef)(null),W=typeof n=="string"?n:p?"":Ue,q=$||(m&&m.trim()?"sdk":"default"),F={html:W,config:N,...p?{templateId:p}:{},showFooter:x,includeUnsubscribe:O,...m?{externalFooterHtml:m}:{},footerInjectionMode:q},J=(0,t.useRef)(G(F)),w=(0,t.useRef)({type:"INIT",payload:F}),Oe=(0,t.useMemo)(()=>ne(_,j),[_,j]),b=(0,t.useCallback)(()=>"*",[]),g=(0,t.useCallback)(e=>{h.current!==e&&(h.current=e,z(e),U==null||U(e))},[U]),c=(0,t.useCallback)((e,o)=>{var l,u;let s=(u=(l=R.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),b())},[b]),Q=(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),b())})},[b]),X=(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),Q())},[Q,P,c,g]),Z=(0,t.useCallback)(async e=>{var o,s;if(e.type==="UPLOAD"){if(!H){console.warn("[EmailBuilderSDK] Upload requested but no onUpload handler is configured.");return}try{let l=await H(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)}}},[H,c]),ee=(0,t.useCallback)(async e=>{var o,s,l;if(e.type==="LIST_ASSETS"){if(!k){c({type:"ASSETS_LIST",payload:{assets:[]}},(o=e.meta)==null?void 0:o.id);return}try{let u=await k(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)}}},[k,c]),te=(0,t.useCallback)(async e=>{var o,s,l;if(e.type==="DELETE_ASSET"){if(!v){c({type:"DELETE_ASSET_SUCCESS",payload:{success:!1}},(o=e.meta)==null?void 0:o.id);return}try{let u=await v(e.payload||{});c({type:"DELETE_ASSET_SUCCESS",payload:{success:!!u}},(s=e.meta)==null?void 0:s.id)}catch(u){console.error("[EmailBuilderSDK] delete asset handler failed",u),c({type:"DELETE_ASSET_SUCCESS",payload:{success:!1}},(l=e.meta)==null?void 0:l.id)}}},[v,c]),V=(0,t.useCallback)(e=>{var l,u,re;let o=(u=(l=R.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":X();break;case"CHANGE":L==null||L(s.payload.html);break;case"SAVE":I==null||I(s.payload.html);break;case"UPLOAD":Z(s);break;case"LIST_ASSETS":ee(s);break;case"DELETE_ASSET":te(s);break;case"AUTH_ERROR":{let ge=((re=s.payload)==null?void 0:re.message)||"Email builder authentication failed";h.current!=="error"&&(h.current="error",z("error"),E==null||E(ge));break}default:break}},[X,Z,ee,te,L,I,E,g]);(0,t.useEffect)(()=>{if(typeof window!="undefined")return window.addEventListener("message",V),()=>{window.removeEventListener("message",V)}},[V]),(0,t.useEffect)(()=>{let e={html:W,config:N,...p?{templateId:p}:{},showFooter:x,includeUnsubscribe:O,...m!==void 0?{externalFooterHtml:m}:{},footerInjectionMode:q},o=G(e);w.current={type:"INIT",payload:e},o!==J.current&&y.current&&c(w.current),J.current=o},[N,W,c,p,x,O,m,$]);let Ee=(0,t.useCallback)(()=>{var s,l;if(d.current=(l=(s=R.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)(de,()=>({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"),ye(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:oe,style:{position:"relative",width:"100%",height:"100%",...le},children:[(0,M.jsx)("iframe",{ref:R,src:pe,title:ue,sandbox:ce,style:Re,loading:"lazy",allowFullScreen:!0,onLoad:Ee},Se),fe!=="ready"&&(0,M.jsx)("div",{style:De,children:"Connecting to builder\u2026"})]})}var Ce=(0,t.forwardRef)(be);0&&(module.exports={EMAIL_BUILDER_PROTOCOL_VERSION,EmailBuilder,createMessageMeta,isMessageLike});
2
2
  //# sourceMappingURL=index.cjs.map
@@ -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 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 className,\n style,\n iframeTitle = 'Email Builder',\n sandbox = DEFAULT_SANDBOX,\n onChange,\n onSave,\n onUpload,\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 iframeSrc = useMemo(() => appendEmbedTokenToSrc(resolvedSrc, embedToken), [resolvedSrc, embedToken]);\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,\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 setStatusSafely('loading');\n const url = await onUpload(eventMessage.payload.file);\n postMessage({ type: 'UPLOAD_SUCCESS', payload: { url } }, eventMessage.meta?.id);\n } catch (error) {\n setStatusSafely('error');\n console.error('[EmailBuilderSDK] Upload handler failed', error);\n } finally {\n if (statusRef.current !== 'error') {\n setStatusSafely('ready');\n }\n }\n },\n [onUpload, postMessage, setStatusSafely]\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 'SAVE':\n onSave?.(message.payload.html);\n break;\n case 'UPLOAD':\n void handleUpload(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, onChange, 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,\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 config,\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 | 'SAVE'\n | 'UPLOAD'\n | 'UPLOAD_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 SavePayload {\n html: string;\n}\n\nexport interface UploadPayload {\n file: File;\n}\n\nexport interface UploadSuccessPayload {\n url: string;\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: 'SAVE'; payload: SavePayload; meta?: MessageMeta }\n | { type: 'UPLOAD'; payload: UploadPayload; meta?: MessageMeta }\n | { type: 'UPLOAD_SUCCESS'; payload: UploadSuccessPayload; meta?: MessageMeta }\n | { type: 'AUTH_ERROR'; payload: AuthErrorPayload; meta?: MessageMeta };\n\nexport type BuilderToHostMessage = Extract<\n Message,\n { type: 'READY' | 'CHANGE' | 'SAVE' | 'UPLOAD' | 'AUTH_ERROR' }\n>;\n\nexport type HostToBuilderMessage = Extract<\n Message,\n { type: 'INIT' | 'UPLOAD_SUCCESS' }\n>;\n\nconst VALID_TYPES: MessageType[] = [\n 'INIT',\n 'READY',\n 'CHANGE',\n 'SAVE',\n 'UPLOAD',\n 'UPLOAD_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","appendEmbedTokenToSrc","src","embedToken","u","sep","EmailBuilderInner","initialHtml","templateId","config","showFooter","includeUnsubscribe","externalFooterHtml","footerInjectionMode","className","style","iframeTitle","sandbox","onChange","onSave","onUpload","onReady","onStatusChange","onAuthError","allowedOrigin","ref","resolvedSrc","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","handleMessage","event","_c","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 iframeTitle = 'Email Builder',\n sandbox = DEFAULT_SANDBOX,\n onChange,\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 '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, 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 | '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 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: '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' | '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 '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","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
@@ -1,7 +1,7 @@
1
1
  import React, { CSSProperties } from 'react';
2
2
 
3
3
  declare const EMAIL_BUILDER_PROTOCOL_VERSION = "1.0.0";
4
- type MessageType = 'INIT' | 'READY' | 'CHANGE' | 'SAVE' | 'UPLOAD' | 'UPLOAD_SUCCESS' | 'AUTH_ERROR';
4
+ type MessageType = 'INIT' | 'READY' | 'CHANGE' | 'SAVE' | 'UPLOAD' | 'UPLOAD_SUCCESS' | 'LIST_ASSETS' | 'ASSETS_LIST' | 'DELETE_ASSET' | 'DELETE_ASSET_SUCCESS' | 'AUTH_ERROR';
5
5
  interface MessageMeta {
6
6
  id: string;
7
7
  correlationId?: string;
@@ -43,6 +43,25 @@ interface UploadPayload {
43
43
  interface UploadSuccessPayload {
44
44
  url: string;
45
45
  }
46
+ interface AssetItem {
47
+ url: string;
48
+ id?: string;
49
+ name?: string;
50
+ thumbnailUrl?: string;
51
+ }
52
+ interface ListAssetsPayload {
53
+ limit?: number;
54
+ }
55
+ interface AssetsListPayload {
56
+ assets: AssetItem[];
57
+ }
58
+ interface DeleteAssetPayload {
59
+ id?: string;
60
+ url?: string;
61
+ }
62
+ interface DeleteAssetSuccessPayload {
63
+ success: boolean;
64
+ }
46
65
  interface AuthErrorPayload {
47
66
  message: string;
48
67
  }
@@ -69,21 +88,39 @@ type Message = {
69
88
  type: 'UPLOAD_SUCCESS';
70
89
  payload: UploadSuccessPayload;
71
90
  meta?: MessageMeta;
91
+ } | {
92
+ type: 'LIST_ASSETS';
93
+ payload?: ListAssetsPayload;
94
+ meta?: MessageMeta;
95
+ } | {
96
+ type: 'ASSETS_LIST';
97
+ payload: AssetsListPayload;
98
+ meta?: MessageMeta;
99
+ } | {
100
+ type: 'DELETE_ASSET';
101
+ payload: DeleteAssetPayload;
102
+ meta?: MessageMeta;
103
+ } | {
104
+ type: 'DELETE_ASSET_SUCCESS';
105
+ payload: DeleteAssetSuccessPayload;
106
+ meta?: MessageMeta;
72
107
  } | {
73
108
  type: 'AUTH_ERROR';
74
109
  payload: AuthErrorPayload;
75
110
  meta?: MessageMeta;
76
111
  };
77
112
  type BuilderToHostMessage = Extract<Message, {
78
- type: 'READY' | 'CHANGE' | 'SAVE' | 'UPLOAD' | 'AUTH_ERROR';
113
+ type: 'READY' | 'CHANGE' | 'SAVE' | 'UPLOAD' | 'LIST_ASSETS' | 'DELETE_ASSET' | 'AUTH_ERROR';
79
114
  }>;
80
115
  type HostToBuilderMessage = Extract<Message, {
81
- type: 'INIT' | 'UPLOAD_SUCCESS';
116
+ type: 'INIT' | 'UPLOAD_SUCCESS' | 'ASSETS_LIST' | 'DELETE_ASSET_SUCCESS';
82
117
  }>;
83
118
  declare function isMessageLike(value: unknown): value is Message;
84
119
  declare function createMessageMeta(correlationId?: string): MessageMeta;
85
120
 
86
121
  type UploadHandler = (file: File) => Promise<string>;
122
+ type ListAssetsHandler = (payload?: ListAssetsPayload) => Promise<AssetItem[]>;
123
+ type DeleteAssetHandler = (payload: DeleteAssetPayload) => Promise<boolean>;
87
124
  type EmailBuilderStatus = 'idle' | 'loading' | 'ready' | 'error';
88
125
  interface EmailBuilderProps {
89
126
  /**
@@ -117,6 +154,10 @@ interface EmailBuilderProps {
117
154
  * - `sdk`: use `externalFooterHtml`
118
155
  */
119
156
  footerInjectionMode?: 'default' | 'sdk';
157
+ /** Render the embedded builder in preview-only mode with just the desktop/mobile switcher. */
158
+ preview?: boolean;
159
+ /** Backwards-compatible alias for `preview`. */
160
+ previewOnly?: boolean;
120
161
  className?: string;
121
162
  style?: CSSProperties;
122
163
  iframeTitle?: string;
@@ -124,6 +165,8 @@ interface EmailBuilderProps {
124
165
  onChange?: (html: string) => void;
125
166
  onSave?: (html: string) => void;
126
167
  onUpload?: UploadHandler;
168
+ onListAssets?: ListAssetsHandler;
169
+ onDeleteAsset?: DeleteAssetHandler;
127
170
  onReady?: () => void;
128
171
  onStatusChange?: (status: EmailBuilderStatus) => void;
129
172
  /** Called when the embedded builder rejects the embed token. */
@@ -139,4 +182,4 @@ interface EmailBuilderHandle {
139
182
 
140
183
  declare const EmailBuilder: React.ForwardRefExoticComponent<EmailBuilderProps & React.RefAttributes<EmailBuilderHandle>>;
141
184
 
142
- export { type AuthErrorPayload, type BuilderConfig, type BuilderToHostMessage, type ChangePayload, EMAIL_BUILDER_PROTOCOL_VERSION, EmailBuilder, type EmailBuilderHandle, type EmailBuilderProps, type EmailBuilderStatus, type HostToBuilderMessage, type InitPayload, type Message, type MessageMeta, type MessageType, type SavePayload, type UploadHandler, type UploadPayload, type UploadSuccessPayload, createMessageMeta, isMessageLike };
185
+ export { type AssetItem, type AssetsListPayload, type AuthErrorPayload, type BuilderConfig, type BuilderToHostMessage, type ChangePayload, type DeleteAssetHandler, type DeleteAssetPayload, type DeleteAssetSuccessPayload, EMAIL_BUILDER_PROTOCOL_VERSION, EmailBuilder, type EmailBuilderHandle, type EmailBuilderProps, type EmailBuilderStatus, type HostToBuilderMessage, type InitPayload, type ListAssetsHandler, type ListAssetsPayload, type Message, type MessageMeta, type MessageType, type SavePayload, type UploadHandler, type UploadPayload, type UploadSuccessPayload, createMessageMeta, isMessageLike };
package/dist/index.d.ts CHANGED
@@ -1,7 +1,7 @@
1
1
  import React, { CSSProperties } from 'react';
2
2
 
3
3
  declare const EMAIL_BUILDER_PROTOCOL_VERSION = "1.0.0";
4
- type MessageType = 'INIT' | 'READY' | 'CHANGE' | 'SAVE' | 'UPLOAD' | 'UPLOAD_SUCCESS' | 'AUTH_ERROR';
4
+ type MessageType = 'INIT' | 'READY' | 'CHANGE' | 'SAVE' | 'UPLOAD' | 'UPLOAD_SUCCESS' | 'LIST_ASSETS' | 'ASSETS_LIST' | 'DELETE_ASSET' | 'DELETE_ASSET_SUCCESS' | 'AUTH_ERROR';
5
5
  interface MessageMeta {
6
6
  id: string;
7
7
  correlationId?: string;
@@ -43,6 +43,25 @@ interface UploadPayload {
43
43
  interface UploadSuccessPayload {
44
44
  url: string;
45
45
  }
46
+ interface AssetItem {
47
+ url: string;
48
+ id?: string;
49
+ name?: string;
50
+ thumbnailUrl?: string;
51
+ }
52
+ interface ListAssetsPayload {
53
+ limit?: number;
54
+ }
55
+ interface AssetsListPayload {
56
+ assets: AssetItem[];
57
+ }
58
+ interface DeleteAssetPayload {
59
+ id?: string;
60
+ url?: string;
61
+ }
62
+ interface DeleteAssetSuccessPayload {
63
+ success: boolean;
64
+ }
46
65
  interface AuthErrorPayload {
47
66
  message: string;
48
67
  }
@@ -69,21 +88,39 @@ type Message = {
69
88
  type: 'UPLOAD_SUCCESS';
70
89
  payload: UploadSuccessPayload;
71
90
  meta?: MessageMeta;
91
+ } | {
92
+ type: 'LIST_ASSETS';
93
+ payload?: ListAssetsPayload;
94
+ meta?: MessageMeta;
95
+ } | {
96
+ type: 'ASSETS_LIST';
97
+ payload: AssetsListPayload;
98
+ meta?: MessageMeta;
99
+ } | {
100
+ type: 'DELETE_ASSET';
101
+ payload: DeleteAssetPayload;
102
+ meta?: MessageMeta;
103
+ } | {
104
+ type: 'DELETE_ASSET_SUCCESS';
105
+ payload: DeleteAssetSuccessPayload;
106
+ meta?: MessageMeta;
72
107
  } | {
73
108
  type: 'AUTH_ERROR';
74
109
  payload: AuthErrorPayload;
75
110
  meta?: MessageMeta;
76
111
  };
77
112
  type BuilderToHostMessage = Extract<Message, {
78
- type: 'READY' | 'CHANGE' | 'SAVE' | 'UPLOAD' | 'AUTH_ERROR';
113
+ type: 'READY' | 'CHANGE' | 'SAVE' | 'UPLOAD' | 'LIST_ASSETS' | 'DELETE_ASSET' | 'AUTH_ERROR';
79
114
  }>;
80
115
  type HostToBuilderMessage = Extract<Message, {
81
- type: 'INIT' | 'UPLOAD_SUCCESS';
116
+ type: 'INIT' | 'UPLOAD_SUCCESS' | 'ASSETS_LIST' | 'DELETE_ASSET_SUCCESS';
82
117
  }>;
83
118
  declare function isMessageLike(value: unknown): value is Message;
84
119
  declare function createMessageMeta(correlationId?: string): MessageMeta;
85
120
 
86
121
  type UploadHandler = (file: File) => Promise<string>;
122
+ type ListAssetsHandler = (payload?: ListAssetsPayload) => Promise<AssetItem[]>;
123
+ type DeleteAssetHandler = (payload: DeleteAssetPayload) => Promise<boolean>;
87
124
  type EmailBuilderStatus = 'idle' | 'loading' | 'ready' | 'error';
88
125
  interface EmailBuilderProps {
89
126
  /**
@@ -117,6 +154,10 @@ interface EmailBuilderProps {
117
154
  * - `sdk`: use `externalFooterHtml`
118
155
  */
119
156
  footerInjectionMode?: 'default' | 'sdk';
157
+ /** Render the embedded builder in preview-only mode with just the desktop/mobile switcher. */
158
+ preview?: boolean;
159
+ /** Backwards-compatible alias for `preview`. */
160
+ previewOnly?: boolean;
120
161
  className?: string;
121
162
  style?: CSSProperties;
122
163
  iframeTitle?: string;
@@ -124,6 +165,8 @@ interface EmailBuilderProps {
124
165
  onChange?: (html: string) => void;
125
166
  onSave?: (html: string) => void;
126
167
  onUpload?: UploadHandler;
168
+ onListAssets?: ListAssetsHandler;
169
+ onDeleteAsset?: DeleteAssetHandler;
127
170
  onReady?: () => void;
128
171
  onStatusChange?: (status: EmailBuilderStatus) => void;
129
172
  /** Called when the embedded builder rejects the embed token. */
@@ -139,4 +182,4 @@ interface EmailBuilderHandle {
139
182
 
140
183
  declare const EmailBuilder: React.ForwardRefExoticComponent<EmailBuilderProps & React.RefAttributes<EmailBuilderHandle>>;
141
184
 
142
- export { type AuthErrorPayload, type BuilderConfig, type BuilderToHostMessage, type ChangePayload, EMAIL_BUILDER_PROTOCOL_VERSION, EmailBuilder, type EmailBuilderHandle, type EmailBuilderProps, type EmailBuilderStatus, type HostToBuilderMessage, type InitPayload, type Message, type MessageMeta, type MessageType, type SavePayload, type UploadHandler, type UploadPayload, type UploadSuccessPayload, createMessageMeta, isMessageLike };
185
+ export { type AssetItem, type AssetsListPayload, type AuthErrorPayload, type BuilderConfig, type BuilderToHostMessage, type ChangePayload, type DeleteAssetHandler, type DeleteAssetPayload, type DeleteAssetSuccessPayload, EMAIL_BUILDER_PROTOCOL_VERSION, EmailBuilder, type EmailBuilderHandle, type EmailBuilderProps, type EmailBuilderStatus, type HostToBuilderMessage, type InitPayload, type ListAssetsHandler, type ListAssetsPayload, type Message, type MessageMeta, type MessageType, type SavePayload, type UploadHandler, type UploadPayload, type UploadSuccessPayload, createMessageMeta, isMessageLike };
package/dist/index.js CHANGED
@@ -1,2 +1,2 @@
1
- import{forwardRef as ge,useCallback as m,useEffect as N,useImperativeHandle as ye,useMemo as X,useRef as d,useState as Z}from"react";var fe="1.0.0",pe=["INIT","READY","CHANGE","SAVE","UPLOAD","UPLOAD_SUCCESS","AUTH_ERROR"];function C(t){if(typeof t!="object"||t===null)return!1;let i=t;if(typeof i.type!="string"||!pe.includes(i.type))return!1;if("meta"in i&&i.meta!==void 0){let r=i.meta;if(r.id&&typeof r.id!="string"||r.correlationId&&typeof r.correlationId!="string"||r.version&&typeof r.version!="string"||r.sentAt&&typeof r.sentAt!="number")return!1}return!0}function J(t){return{id:typeof crypto!="undefined"&&"randomUUID"in crypto?crypto.randomUUID():Math.random().toString(36).slice(2),correlationId:t,version:fe,sentAt:Date.now()}}function Q(t,i){if(i){let f=new URL(i);if(f.origin==="null")throw new Error("allowedOrigin must resolve to a valid origin");return f.origin}let r;try{r=new URL(t)}catch{throw new Error("EmailBuilder `src` must be an absolute URL (e.g. https://example.com)")}if(!r.origin||r.origin==="null")throw new Error("EmailBuilder requires an absolute src URL with a valid origin");return r.origin}function T(t,i){let r=J(i);return{...t,meta:r}}function v(t){return JSON.stringify(t!=null?t:null)}import{jsx as ee,jsxs as Re}from"react/jsx-runtime";var me="allow-scripts allow-same-origin allow-forms",we="<h1>Hello World</h1><p>Start building your email template.</p>",Me="https://pc-email-template-builder.netlify.app",Ee={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},Se={border:"none",width:"100%",height:"100%"};function Ie(t,i){if(!i||!i.trim())return t;try{let r=new URL(t,typeof window!="undefined"?window.location.href:"https://example.com");return r.searchParams.set("embedToken",i),r.toString()}catch{let r=t.includes("?")?"&":"?";return`${t}${r}embedToken=${encodeURIComponent(i)}`}}function Te({src:t,initialHtml:i,embedToken:r,templateId:f,config:D,showFooter:O,includeUnsubscribe:x,externalFooterHtml:w,footerInjectionMode:W,className:re,style:te,iframeTitle:ne="Email Builder",sandbox:ie=me,onChange:R,onSave:U,onUpload:H,onReady:P,onStatusChange:A,onAuthError:g,allowedOrigin:V},ae){let B=(t==null?void 0:t.trim())||Me,se=X(()=>Ie(B,r),[B,r]),L=d(null),o=d(null),p=d(!1),E=d("loading"),[oe,Y]=Z("loading"),[le,ue]=Z(0),h=d([]),S=d(null),l=d(null),n=d(null),k=typeof i=="string"?i:f?"":we,G=W||(w&&w.trim()?"sdk":"default"),j={html:k,config:D,...f?{templateId:f}:{},showFooter:O,includeUnsubscribe:x,...w?{externalFooterHtml:w}:{},footerInjectionMode:G},z=d(v(j)),I=d({type:"INIT",payload:j}),Ue=X(()=>Q(B,V),[B,V]),b=m(()=>"*",[]),u=m(e=>{E.current!==e&&(E.current=e,Y(e),A==null||A(e))},[A]),M=m((e,s)=>{var c,y;let a=(y=(c=L.current)==null?void 0:c.contentWindow)!=null?y:o.current;if(a&&(o.current=a),!o.current||!p.current){h.current.push({message:e,correlationId:s});return}o.current.postMessage(T(e,s),b())},[b]),K=m(()=>{if(!p.current||!o.current)return;let e=[...h.current];h.current=[],e.forEach(({message:s,correlationId:a})=>{var c;(c=o.current)==null||c.postMessage(T(s,a),b())})},[b]),q=m(()=>{p.current||(l.current&&(window.clearTimeout(l.current),l.current=null),n.current&&(window.clearInterval(n.current),n.current=null),p.current=!0,u("ready"),P==null||P(),M(I.current),K())},[K,P,M,u]),F=m(async e=>{var s;if(e.type==="UPLOAD"){if(!H){console.warn("[EmailBuilderSDK] Upload requested but no onUpload handler is configured.");return}try{u("loading");let a=await H(e.payload.file);M({type:"UPLOAD_SUCCESS",payload:{url:a}},(s=e.meta)==null?void 0:s.id)}catch(a){u("error"),console.error("[EmailBuilderSDK] Upload handler failed",a)}finally{E.current!=="error"&&u("ready")}}},[H,M,u]),_=m(e=>{var c,y,$;let s=(y=(c=L.current)==null?void 0:c.contentWindow)!=null?y:o.current;if(!s||e.source!==s||!C(e.data))return;if(!S.current)S.current=e.origin;else if(e.origin!==S.current)return;let a=e.data;switch(e.source&&e.source!==o.current&&(o.current=e.source),a.type){case"READY":q();break;case"CHANGE":R==null||R(a.payload.html);break;case"SAVE":U==null||U(a.payload.html);break;case"UPLOAD":F(a);break;case"AUTH_ERROR":{let de=(($=a.payload)==null?void 0:$.message)||"Email builder authentication failed";E.current!=="error"&&(E.current="error",Y("error"),g==null||g(de));break}default:break}},[q,F,R,U,g,u]);N(()=>{if(typeof window!="undefined")return window.addEventListener("message",_),()=>{window.removeEventListener("message",_)}},[_]),N(()=>{let e={html:k,config:D,...f?{templateId:f}:{},showFooter:O,includeUnsubscribe:x,...w!==void 0?{externalFooterHtml:w}:{},footerInjectionMode:G},s=v(e);I.current={type:"INIT",payload:e},s!==z.current&&p.current&&M(I.current),z.current=s},[D,k,M,f,O,x,w,W]);let ce=m(()=>{var a,c;if(o.current=(c=(a=L.current)==null?void 0:a.contentWindow)!=null?c:null,p.current=!1,S.current=null,u("loading"),o.current)try{o.current.postMessage(T(I.current),"*")}catch{}l.current&&window.clearTimeout(l.current),n.current&&window.clearInterval(n.current);let e=0,s=12;n.current=window.setInterval(()=>{var y;if(p.current){n.current&&(window.clearInterval(n.current),n.current=null);return}e+=1;try{(y=o.current)==null||y.postMessage(T(I.current),"*")}catch{}e>=s&&n.current&&(window.clearInterval(n.current),n.current=null)},1e3),l.current=window.setTimeout(()=>{p.current||(n.current&&(window.clearInterval(n.current),n.current=null),u("error"),g==null||g("Builder handshake failed or authentication was rejected."))},12e3)},[g,u]);return ye(ae,()=>({reload(){h.current=[],p.current=!1,o.current=null,S.current=null,l.current&&(window.clearTimeout(l.current),l.current=null),n.current&&(window.clearInterval(n.current),n.current=null),u("loading"),ue(e=>e+1)}}),[u]),N(()=>()=>{l.current&&(window.clearTimeout(l.current),l.current=null),n.current&&(window.clearInterval(n.current),n.current=null)},[]),Re("div",{className:re,style:{position:"relative",width:"100%",height:"100%",...te},children:[ee("iframe",{ref:L,src:se,title:ne,sandbox:ie,style:Se,loading:"lazy",allowFullScreen:!0,onLoad:ce},le),oe!=="ready"&&ee("div",{style:Ee,children:"Connecting to builder\u2026"})]})}var He=ge(Te);export{fe as EMAIL_BUILDER_PROTOCOL_VERSION,He as EmailBuilder,J as createMessageMeta,C as isMessageLike};
1
+ import{forwardRef as Me,useCallback as p,useEffect as Y,useImperativeHandle as Le,useMemo as G,useRef as f,useState as se}from"react";var we="1.0.0",Ae=["INIT","READY","CHANGE","SAVE","UPLOAD","UPLOAD_SUCCESS","LIST_ASSETS","ASSETS_LIST","DELETE_ASSET","DELETE_ASSET_SUCCESS","AUTH_ERROR"];function V(r){if(typeof r!="object"||r===null)return!1;let n=r;if(typeof n.type!="string"||!Ae.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 ae(r){return{id:typeof crypto!="undefined"&&"randomUUID"in crypto?crypto.randomUUID():Math.random().toString(36).slice(2),correlationId:r,version:we,sentAt:Date.now()}}function ne(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=ae(n);return{...r,meta:t}}function K(r){return JSON.stringify(r!=null?r:null)}import{jsx as ie,jsxs as be}from"react/jsx-runtime";var Ie="allow-scripts allow-same-origin allow-forms",Pe="<h1>Hello World</h1><p>Start building your email template.</p>",Ue="https://pc-email-template-builder.netlify.app",_e={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},De={border:"none",width:"100%",height:"100%"};function Re(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 he(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 Be({src:r,initialHtml:n,embedToken:t,templateId:S,config:T,showFooter:C,includeUnsubscribe:x,externalFooterHtml:m,footerInjectionMode:$,preview:oe=!1,previewOnly:le=!1,className:ue,style:ce,iframeTitle:de="Email Builder",sandbox:pe=Ie,onChange:L,onSave:I,onUpload:O,onListAssets:H,onDeleteAsset:k,onReady:P,onStatusChange:U,onAuthError:E,allowedOrigin:j},fe){let _=(r==null?void 0:r.trim())||Ue,D=oe||le,v=G(()=>D?{...T!=null?T:{},preview:!0,previewOnly:!0}:T,[T,D]),Se=G(()=>he(Re(_,D),t),[_,t,D]),R=f(null),c=f(null),y=f(!1),h=f("loading"),[ye,z]=se("loading"),[Ee,ge]=se(0),B=f([]),w=f(null),d=f(null),s=f(null),N=typeof n=="string"?n:S?"":Pe,q=$||(m&&m.trim()?"sdk":"default"),F={html:N,config:v,...S?{templateId:S}:{},showFooter:C,includeUnsubscribe:x,...m?{externalFooterHtml:m}:{},footerInjectionMode:q},J=f(K(F)),A=f({type:"INIT",payload:F}),Ce=G(()=>ne(_,j),[_,j]),b=p(()=>"*",[]),g=p(e=>{h.current!==e&&(h.current=e,z(e),U==null||U(e))},[U]),u=p((e,i)=>{var o,l;let a=(l=(o=R.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),b())},[b]),Q=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),b())})},[b]),X=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),Q())},[Q,P,u,g]),Z=p(async e=>{var i,a;if(e.type==="UPLOAD"){if(!O){console.warn("[EmailBuilderSDK] Upload requested but no onUpload handler is configured.");return}try{let o=await O(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)}}},[O,u]),ee=p(async e=>{var i,a,o;if(e.type==="LIST_ASSETS"){if(!H){u({type:"ASSETS_LIST",payload:{assets:[]}},(i=e.meta)==null?void 0:i.id);return}try{let l=await H(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)}}},[H,u]),te=p(async e=>{var i,a,o;if(e.type==="DELETE_ASSET"){if(!k){u({type:"DELETE_ASSET_SUCCESS",payload:{success:!1}},(i=e.meta)==null?void 0:i.id);return}try{let l=await k(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)}}},[k,u]),W=p(e=>{var o,l,re;let i=(l=(o=R.current)==null?void 0:o.contentWindow)!=null?l:c.current;if(!i||e.source!==i||!V(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":X();break;case"CHANGE":L==null||L(a.payload.html);break;case"SAVE":I==null||I(a.payload.html);break;case"UPLOAD":Z(a);break;case"LIST_ASSETS":ee(a);break;case"DELETE_ASSET":te(a);break;case"AUTH_ERROR":{let Te=((re=a.payload)==null?void 0:re.message)||"Email builder authentication failed";h.current!=="error"&&(h.current="error",z("error"),E==null||E(Te));break}default:break}},[X,Z,ee,te,L,I,E,g]);Y(()=>{if(typeof window!="undefined")return window.addEventListener("message",W),()=>{window.removeEventListener("message",W)}},[W]),Y(()=>{let e={html:N,config:v,...S?{templateId:S}:{},showFooter:C,includeUnsubscribe:x,...m!==void 0?{externalFooterHtml:m}:{},footerInjectionMode:q},i=K(e);A.current={type:"INIT",payload:e},i!==J.current&&y.current&&u(A.current),J.current=i},[v,N,u,S,C,x,m,$]);let me=p(()=>{var a,o;if(c.current=(o=(a=R.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 Le(fe,()=>({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"),ge(e=>e+1)}}),[g]),Y(()=>()=>{d.current&&(window.clearTimeout(d.current),d.current=null),s.current&&(window.clearInterval(s.current),s.current=null)},[]),be("div",{className:ue,style:{position:"relative",width:"100%",height:"100%",...ce},children:[ie("iframe",{ref:R,src:Se,title:de,sandbox:pe,style:De,loading:"lazy",allowFullScreen:!0,onLoad:me},Ee),ye!=="ready"&&ie("div",{style:_e,children:"Connecting to builder\u2026"})]})}var Ye=Me(Be);export{we as EMAIL_BUILDER_PROTOCOL_VERSION,Ye as EmailBuilder,ae as createMessageMeta,V 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 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 className,\n style,\n iframeTitle = 'Email Builder',\n sandbox = DEFAULT_SANDBOX,\n onChange,\n onSave,\n onUpload,\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 iframeSrc = useMemo(() => appendEmbedTokenToSrc(resolvedSrc, embedToken), [resolvedSrc, embedToken]);\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,\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 setStatusSafely('loading');\n const url = await onUpload(eventMessage.payload.file);\n postMessage({ type: 'UPLOAD_SUCCESS', payload: { url } }, eventMessage.meta?.id);\n } catch (error) {\n setStatusSafely('error');\n console.error('[EmailBuilderSDK] Upload handler failed', error);\n } finally {\n if (statusRef.current !== 'error') {\n setStatusSafely('ready');\n }\n }\n },\n [onUpload, postMessage, setStatusSafely]\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 'SAVE':\n onSave?.(message.payload.html);\n break;\n case 'UPLOAD':\n void handleUpload(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, onChange, 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,\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 config,\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 | 'SAVE'\n | 'UPLOAD'\n | 'UPLOAD_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 SavePayload {\n html: string;\n}\n\nexport interface UploadPayload {\n file: File;\n}\n\nexport interface UploadSuccessPayload {\n url: string;\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: 'SAVE'; payload: SavePayload; meta?: MessageMeta }\n | { type: 'UPLOAD'; payload: UploadPayload; meta?: MessageMeta }\n | { type: 'UPLOAD_SUCCESS'; payload: UploadSuccessPayload; meta?: MessageMeta }\n | { type: 'AUTH_ERROR'; payload: AuthErrorPayload; meta?: MessageMeta };\n\nexport type BuilderToHostMessage = Extract<\n Message,\n { type: 'READY' | 'CHANGE' | 'SAVE' | 'UPLOAD' | 'AUTH_ERROR' }\n>;\n\nexport type HostToBuilderMessage = Extract<\n Message,\n { type: 'INIT' | 'UPLOAD_SUCCESS' }\n>;\n\nconst VALID_TYPES: MessageType[] = [\n 'INIT',\n 'READY',\n 'CHANGE',\n 'SAVE',\n 'UPLOAD',\n 'UPLOAD_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","appendEmbedTokenToSrc","src","embedToken","u","sep","EmailBuilderInner","initialHtml","templateId","config","showFooter","includeUnsubscribe","externalFooterHtml","footerInjectionMode","className","style","iframeTitle","sandbox","onChange","onSave","onUpload","onReady","onStatusChange","onAuthError","allowedOrigin","ref","resolvedSrc","iframeSrc","useMemo","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","handleMessage","event","_c","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 iframeTitle = 'Email Builder',\n sandbox = DEFAULT_SANDBOX,\n onChange,\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 '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, 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 | '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 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: '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' | '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 '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","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"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@alihaiderrana/email-builder-sdk",
3
- "version": "0.1.11",
3
+ "version": "0.1.13",
4
4
  "description": "React iframe SDK for embedding the Circles email template builder",
5
5
  "license": "MIT",
6
6
  "type": "module",