forgeframe 0.0.3 → 0.0.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/communication/bridge.d.ts +2 -2
- package/dist/communication/messenger.d.ts +9 -0
- package/dist/core/component.d.ts +8 -0
- package/dist/core/consumer.d.ts +67 -1
- package/dist/core/host.d.ts +5 -0
- package/dist/forgeframe.js +746 -459
- package/dist/forgeframe.umd.cjs +2 -2
- package/dist/props/prop.d.ts +2 -0
- package/dist/types.d.ts +11 -5
- package/dist/utils/cleanup.d.ts +4 -3
- package/dist/window/helpers.d.ts +5 -1
- package/package.json +1 -1
package/dist/forgeframe.umd.cjs
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
(function(u,p){typeof exports=="object"&&typeof module<"u"?p(exports):typeof define=="function"&&define.amd?define(["exports"],p):(u=typeof globalThis<"u"?globalThis:u||self,p(u.ForgeFrame={}))})(this,(function(u){"use strict";const p={IFRAME:"iframe",POPUP:"popup"},m={RENDER:"render",RENDERED:"rendered",PRERENDER:"prerender",PRERENDERED:"prerendered",DISPLAY:"display",ERROR:"error",CLOSE:"close",DESTROY:"destroy",PROPS:"props",RESIZE:"resize",FOCUS:"focus"},v={JSON:"json",BASE64:"base64",DOTIFY:"dotify"},N={REQUEST:"request",RESPONSE:"response"},h={INIT:"forgeframe_init",PROPS:"forgeframe_props",CLOSE:"forgeframe_close",RESIZE:"forgeframe_resize",FOCUS:"forgeframe_focus",SHOW:"forgeframe_show",HIDE:"forgeframe_hide",ERROR:"forgeframe_error",EXPORT:"forgeframe_export",CALL:"forgeframe_call",CONSUMER_EXPORT:"forgeframe_consumer_export",GET_SIBLINGS:"forgeframe_get_siblings"},k="__forgeframe__",q=(()=>{if("0.0.3".trim().length===0)throw new Error("ForgeFrame VERSION injection is missing. Configure __FORGEFRAME_VERSION__ in build/test tooling.");return"0.0.3"})();class le{listeners=new Map;on(e,t){return this.listeners.has(e)||this.listeners.set(e,new Set),this.listeners.get(e).add(t),()=>this.off(e,t)}once(e,t){const s=i=>(this.off(e,s),t(i));return this.on(e,s)}emit(e,t){const s=this.listeners.get(e);if(s)for(const i of s)try{const r=i(t);r&&typeof r=="object"&&"catch"in r&&typeof r.catch=="function"&&r.catch(o=>{console.error(`Error in async event handler for "${e}":`,o)})}catch(r){console.error(`Error in event handler for "${e}":`,r)}}off(e,t){if(!t){this.listeners.delete(e);return}const s=this.listeners.get(e);s&&(s.delete(t),s.size===0&&this.listeners.delete(e))}removeAllListeners(){this.listeners.clear()}listenerCount(e){return this.listeners.get(e)?.size??0}}function He(){const n=Date.now().toString(36),e=Math.random().toString(36).slice(2,11);return`${n}_${e}`}function J(){return Math.random().toString(36).slice(2,11)}class Ae{tasks=[];cleaned=!1;register(e){if(this.cleaned){try{e()}catch(t){console.error("Error in cleanup task:",t)}return}this.tasks.push(e)}async cleanup(){if(this.cleaned)return;this.cleaned=!0;const e=this.tasks.reverse();this.tasks=[];for(const t of e)try{await t()}catch(s){console.error("Error in cleanup task:",s)}}isCleaned(){return this.cleaned}reset(){this.tasks=[],this.cleaned=!1}}function ue(){let n,e;return{promise:new Promise((s,i)=>{n=s,e=i}),resolve:n,reject:e}}function Ue(n,e,t="Operation timed out"){return new Promise((s,i)=>{const r=setTimeout(()=>{i(new Error(`${t} (${e}ms)`))},e);n.then(o=>{clearTimeout(r),s(o)}).catch(o=>{clearTimeout(r),i(o)})})}const X="forgeframe:";function K(n){return X+JSON.stringify(n)}function Me(n){if(typeof n!="string"||!n.startsWith(X))return null;try{const e=n.slice(X.length),t=JSON.parse(e);return!t.id||!t.type||!t.name||!t.source?null:t}catch{return null}}function he(n,e,t,s){return{id:n,type:N.REQUEST,name:e,data:t,source:s}}function We(n,e,t,s){return{id:n,type:N.RESPONSE,name:"response",data:e,source:t,error:s?{message:s.message,stack:s.stack}:void 0}}class de{constructor(e,t=window,s=window.location.origin,i){this.uid=e,this.win=t,this.domain=s,this.allowedOrigins.add(s),i&&this.addTrustedDomain(i),this.setupListener()}pending=new Map;handlers=new Map;listener=null;destroyed=!1;allowedOrigins=new Set;allowedOriginPatterns=[];addTrustedDomain(e){if(Array.isArray(e))for(const t of e)this.allowedOrigins.add(t);else e instanceof RegExp?this.allowedOriginPatterns.push(e):this.allowedOrigins.add(e)}removeTrustedDomain(e){if(Array.isArray(e))for(const t of e)this.allowedOrigins.delete(t);else e instanceof RegExp?this.allowedOriginPatterns=this.allowedOriginPatterns.filter(t=>t!==e):this.allowedOrigins.delete(e)}isOriginTrusted(e){if(this.allowedOrigins.has(e))return!0;for(const t of this.allowedOriginPatterns)if(t.test(e))return!0;return!1}async send(e,t,s,i,r=1e4){if(this.destroyed)throw new Error("Messenger has been destroyed");const o=J(),a=he(o,s,i,{uid:this.uid,domain:this.domain}),c=ue(),l=setTimeout(()=>{this.pending.delete(o),c.reject(new Error(`Message "${s}" timed out after ${r}ms`))},r);this.pending.set(o,{deferred:c,timeout:l});try{e.postMessage(K(a),t)}catch(d){throw this.pending.delete(o),clearTimeout(l),d}return c.promise}post(e,t,s,i){if(this.destroyed)throw new Error("Messenger has been destroyed");const r=J(),o=he(r,s,i,{uid:this.uid,domain:this.domain});e.postMessage(K(o),t)}on(e,t){return this.handlers.set(e,t),()=>this.handlers.delete(e)}setupListener(){this.listener=e=>{if(e.source===this.win||!this.isOriginTrusted(e.origin))return;const t=Me(e.data);t&&this.handleMessage(t,e.source,e.origin)},this.win.addEventListener("message",this.listener)}async handleMessage(e,t,s){if(e.type===N.RESPONSE){const i=this.pending.get(e.id);if(i)if(this.pending.delete(e.id),clearTimeout(i.timeout),e.error){const r=new Error(e.error.message);r.stack=e.error.stack,i.deferred.reject(r)}else i.deferred.resolve(e.data);return}if(e.type===N.REQUEST){const i=this.handlers.get(e.name);if(!i)return;let r,o;try{r=await i(e.data,e.source)}catch(c){o=c instanceof Error?c:new Error(String(c))}const a=We(e.id,r,{uid:this.uid,domain:this.domain},o);try{t.postMessage(K(a),s)}catch{}}}destroy(){if(!this.destroyed){this.destroyed=!0,this.listener&&(this.win.removeEventListener("message",this.listener),this.listener=null);for(const e of this.pending.values())clearTimeout(e.timeout),e.deferred.reject(new Error("Messenger destroyed"));this.pending.clear(),this.handlers.clear()}}isDestroyed(){return this.destroyed}}const fe=500;class Z{constructor(e){this.messenger=e,this.setupCallHandler()}localFunctions=new Map;remoteFunctions=new Map;currentBatchIds=new Set;serialize(e,t){if(this.localFunctions.size>=fe){const i=this.localFunctions.keys().next().value;i&&this.localFunctions.delete(i)}const s=J();return this.localFunctions.set(s,e),this.currentBatchIds.add(s),{__type__:"function",__id__:s,__name__:t||e.name||"anonymous"}}deserialize(e,t,s){const i=`${e.__id__}`,r=this.remoteFunctions.get(i);if(r)return r;if(this.remoteFunctions.size>=fe){const a=this.remoteFunctions.keys().next().value;a&&this.remoteFunctions.delete(a)}const o=async(...a)=>this.messenger.send(t,s,h.CALL,{id:e.__id__,args:a});return Object.defineProperty(o,"name",{value:e.__name__,configurable:!0}),this.remoteFunctions.set(i,o),o}static isFunctionRef(e){return typeof e=="object"&&e!==null&&e.__type__==="function"&&typeof e.__id__=="string"}setupCallHandler(){this.messenger.on(h.CALL,async({id:e,args:t})=>{const s=this.localFunctions.get(e);if(!s)throw new Error(`Function with id "${e}" not found`);return s(...t)})}removeLocal(e){this.localFunctions.delete(e)}startBatch(){this.currentBatchIds.clear()}finishBatch(e=!1){if(e){this.currentBatchIds.clear();return}for(const t of this.localFunctions.keys())this.currentBatchIds.has(t)||this.localFunctions.delete(t);this.currentBatchIds.clear()}clearRemote(){this.remoteFunctions.clear()}get localFunctionCount(){return this.localFunctions.size}get remoteFunctionCount(){return this.remoteFunctions.size}destroy(){this.localFunctions.clear(),this.remoteFunctions.clear(),this.currentBatchIds.clear()}}function G(n,e,t=new WeakSet){if(typeof n=="function")return e.serialize(n);if(Array.isArray(n)){if(t.has(n))throw new Error("Circular reference detected in props - arrays cannot contain circular references");return t.add(n),n.map(s=>G(s,e,t))}if(typeof n=="object"&&n!==null){if(t.has(n))throw new Error("Circular reference detected in props - objects cannot contain circular references");t.add(n);const s={};for(const[i,r]of Object.entries(n))s[i]=G(r,e,t);return s}return n}function Y(n,e,t,s,i=new WeakSet){if(Z.isFunctionRef(n))return e.deserialize(n,t,s);if(Array.isArray(n)){if(i.has(n))throw new Error("Circular reference detected in serialized props");return i.add(n),n.map(r=>Y(r,e,t,s,i))}if(typeof n=="object"&&n!==null){if(i.has(n))throw new Error("Circular reference detected in serialized props");i.add(n);const r={};for(const[o,a]of Object.entries(n))r[o]=Y(a,e,t,s,i);return r}return n}function Q(n=window){try{return n.location.origin}catch{return""}}function pe(n,e=window){try{return n.location.origin===e.location.origin}catch{return!1}}function $(n,e){return typeof n=="string"?n==="*"?!0:n===e:n instanceof RegExp?n.test(e):Array.isArray(n)?n.some(t=>$(t,e)):!1}function me(n){if(!n)return!0;try{return n.closed}catch{return!0}}function ze(n=window){try{return n.opener}catch{return null}}function je(n=window){try{const e=n.parent;return e&&e!==n?e:null}catch{return null}}function Be(n=window){try{return n.parent!==n}catch{return!0}}function Ve(n=window){try{return n.opener!==null&&n.opener!==void 0}catch{return!1}}const ge=32*1024;function qe(n){const e=Je(n);return`${k}${e}`}function ye(n){if(!n||!n.startsWith(k))return null;const e=n.slice(k.length);return Xe(e)}function ee(n=window){try{return n.name.startsWith(k)}catch{return!1}}function te(n,e=window){return ye(e.name)?.tag===n}function Je(n){try{const e=JSON.stringify(n),t=btoa(encodeURIComponent(e)),s=new Blob([t]).size;if(s>ge)throw new Error(`Payload size (${Math.round(s/1024)}KB) exceeds maximum allowed size (${ge/1024}KB). Consider reducing the amount of data passed via props.`);return t}catch(e){throw e instanceof Error&&e.message.includes("Payload size")?e:new Error(`Failed to encode payload: ${e}`)}}function Xe(n){try{const e=decodeURIComponent(atob(n));return JSON.parse(e)}catch{return null}}function Ke(n){return{uid:n.uid,tag:n.tag,version:q,context:n.context,consumerDomain:n.consumerDomain,props:n.props,exports:n.exports,children:n.children}}function Ze(n=window){return ye(n.name)}const we=100,E=new Map;function Ge(){const n=[];for(const[e,t]of E.entries())me(t)&&n.push(e);for(const e of n)E.delete(e)}function Ye(n,e){if(E.size>=we&&Ge(),E.size>=we){const t=E.keys().next().value;t&&E.delete(t)}E.set(n,e)}function Qe(n){E.delete(n)}class g{_optional=!1;_nullable=!1;_default;"~standard"={version:1,vendor:"forgeframe",validate:e=>e===null?this._nullable?{value:null}:{issues:[{message:"Expected a value, got null"}]}:e===void 0?this._default!==void 0?{value:typeof this._default=="function"?this._default():this._default}:this._optional?{value:void 0}:{issues:[{message:"Required"}]}:this._validate(e)};optional(){const e=this._clone();return e._optional=!0,e}nullable(){const e=this._clone();return e._nullable=!0,e}default(e){const t=this._clone();return t._default=e,t}}class L extends g{_minLength;_maxLength;_pattern;_patternMessage;_trim=!1;_validate(e){if(typeof e!="string")return{issues:[{message:`Expected string, got ${typeof e}`}]};const t=this._trim?e.trim():e;return this._minLength!==void 0&&t.length<this._minLength?{issues:[{message:`String must be at least ${this._minLength} characters`}]}:this._maxLength!==void 0&&t.length>this._maxLength?{issues:[{message:`String must be at most ${this._maxLength} characters`}]}:this._pattern&&!this._pattern.test(t)?{issues:[{message:this._patternMessage||`String must match pattern ${this._pattern}`}]}:{value:t}}_clone(){const e=new L;return e._optional=this._optional,e._nullable=this._nullable,e._default=this._default,e._minLength=this._minLength,e._maxLength=this._maxLength,e._pattern=this._pattern,e._patternMessage=this._patternMessage,e._trim=this._trim,e}min(e){const t=this._clone();return t._minLength=e,t}max(e){const t=this._clone();return t._maxLength=e,t}length(e){const t=this._clone();return t._minLength=e,t._maxLength=e,t}pattern(e,t){const s=this._clone();return s._pattern=e,s._patternMessage=t,s}email(){return this.pattern(/^[^\s@]+@[^\s@]+\.[^\s@]+$/,"Invalid email address")}url(){return this.pattern(/^https?:\/\/.+/,"Invalid URL")}uuid(){return this.pattern(/^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i,"Invalid UUID")}trim(){const e=this._clone();return e._trim=!0,e}nonempty(){const e=this._clone();return e._minLength=1,e}}class H extends g{_min;_max;_int=!1;_validate(e){return typeof e!="number"||Number.isNaN(e)?{issues:[{message:`Expected number, got ${typeof e}`}]}:this._int&&!Number.isInteger(e)?{issues:[{message:"Expected integer"}]}:this._min!==void 0&&e<this._min?{issues:[{message:`Number must be >= ${this._min}`}]}:this._max!==void 0&&e>this._max?{issues:[{message:`Number must be <= ${this._max}`}]}:{value:e}}_clone(){const e=new H;return e._optional=this._optional,e._nullable=this._nullable,e._default=this._default,e._min=this._min,e._max=this._max,e._int=this._int,e}min(e){const t=this._clone();return t._min=e,t}max(e){const t=this._clone();return t._max=e,t}int(){const e=this._clone();return e._int=!0,e}positive(){const e=this._clone();return e._min=Number.MIN_VALUE,e}nonnegative(){const e=this._clone();return e._min=0,e}negative(){const e=this._clone();return e._max=-Number.MIN_VALUE,e}}class A extends g{_validate(e){return typeof e!="boolean"?{issues:[{message:`Expected boolean, got ${typeof e}`}]}:{value:e}}_clone(){const e=new A;return e._optional=this._optional,e._nullable=this._nullable,e._default=this._default,e}}class U extends g{_validate(e){return typeof e!="function"?{issues:[{message:`Expected function, got ${typeof e}`}]}:{value:e}}_clone(){const e=new U;return e._optional=this._optional,e._nullable=this._nullable,e._default=this._default,e}}class C extends g{_itemSchema;_minLength;_maxLength;_validate(e){if(!Array.isArray(e))return{issues:[{message:`Expected array, got ${typeof e}`}]};if(this._minLength!==void 0&&e.length<this._minLength)return{issues:[{message:`Array must have at least ${this._minLength} items`}]};if(this._maxLength!==void 0&&e.length>this._maxLength)return{issues:[{message:`Array must have at most ${this._maxLength} items`}]};if(this._itemSchema){const t=[];for(let s=0;s<e.length;s++){const i=this._itemSchema["~standard"].validate(e[s]);if(i instanceof Promise)throw new Error("Async schema validation is not supported. Use synchronous schemas.");if(i.issues)return{issues:i.issues.map(r=>({...r,path:[s,...r.path||[]]}))};t.push(i.value)}return{value:t}}return{value:e}}_clone(){const e=new C;return e._optional=this._optional,e._nullable=this._nullable,e._default=this._default,e._itemSchema=this._itemSchema,e._minLength=this._minLength,e._maxLength=this._maxLength,e}of(e){const t=new C;return t._optional=this._optional,t._nullable=this._nullable,t._itemSchema=e,t._minLength=this._minLength,t._maxLength=this._maxLength,t}min(e){const t=this._clone();return t._minLength=e,t}max(e){const t=this._clone();return t._maxLength=e,t}nonempty(){return this.min(1)}}class D extends g{_shape;_strict=!1;_validate(e){if(typeof e!="object"||e===null||Array.isArray(e))return{issues:[{message:`Expected object, got ${Array.isArray(e)?"array":typeof e}`}]};const t=e,s={};if(this._shape){if(this._strict){const i=new Set(Object.keys(this._shape));for(const r of Object.keys(t))if(!i.has(r))return{issues:[{message:`Unknown key: ${r}`,path:[r]}]}}for(const[i,r]of Object.entries(this._shape)){const o=r["~standard"].validate(t[i]);if(o instanceof Promise)throw new Error("Async schema validation is not supported. Use synchronous schemas.");if(o.issues)return{issues:o.issues.map(a=>({...a,path:[i,...a.path||[]]}))};s[i]=o.value}if(!this._strict)for(const i of Object.keys(t))i in this._shape||(s[i]=t[i]);return{value:s}}return{value:e}}_clone(){const e=new D;return e._optional=this._optional,e._nullable=this._nullable,e._default=this._default,e._shape=this._shape,e._strict=this._strict,e}shape(e){const t=new D;return t._optional=this._optional,t._nullable=this._nullable,t._shape=e,t._strict=this._strict,t}strict(){const e=this._clone();return e._strict=!0,e}}class M extends g{_value;constructor(e){super(),this._value=e}_validate(e){return e!==this._value?{issues:[{message:`Expected ${JSON.stringify(this._value)}, got ${JSON.stringify(e)}`}]}:{value:e}}_clone(){const e=new M(this._value);return e._optional=this._optional,e._nullable=this._nullable,e._default=this._default,e}}class W extends g{_values;constructor(e){super(),this._values=e}_validate(e){return this._values.includes(e)?{value:e}:{issues:[{message:`Expected one of [${this._values.map(t=>JSON.stringify(t)).join(", ")}], got ${JSON.stringify(e)}`}]}}_clone(){const e=new W(this._values);return e._optional=this._optional,e._nullable=this._nullable,e._default=this._default,e}}class z extends g{constructor(){super(),this._nullable=!0}_validate(e){return{value:e}}_clone(){const e=new z;return e._optional=this._optional,e._nullable=this._nullable,e._default=this._default,e}}const f={string:()=>new L,number:()=>new H,boolean:()=>new A,function:()=>new U,array:()=>new C,object:()=>new D,literal:n=>new M(n),enum:n=>new W(n),any:()=>new z},O={uid:{schema:f.string().optional(),sendToHost:!0},tag:{schema:f.string().optional(),sendToHost:!0},dimensions:{schema:f.object().default(()=>({width:"100%",height:"100%"})),sendToHost:!1},timeout:{schema:f.number().default(1e4),sendToHost:!1},cspNonce:{schema:f.string().optional(),sendToHost:!0},onDisplay:{schema:f.function().optional(),sendToHost:!1},onRendered:{schema:f.function().optional(),sendToHost:!1},onRender:{schema:f.function().optional(),sendToHost:!1},onPrerendered:{schema:f.function().optional(),sendToHost:!1},onPrerender:{schema:f.function().optional(),sendToHost:!1},onClose:{schema:f.function().optional(),sendToHost:!1},onDestroy:{schema:f.function().optional(),sendToHost:!1},onResize:{schema:f.function().optional(),sendToHost:!1},onFocus:{schema:f.function().optional(),sendToHost:!1},onError:{schema:f.function().optional(),sendToHost:!1},onProps:{schema:f.function().optional(),sendToHost:!1}};function w(n){return typeof n=="object"&&n!==null&&"~standard"in n&&typeof n["~standard"]=="object"&&n["~standard"]!==null&&n["~standard"].version===1&&typeof n["~standard"].vendor=="string"&&typeof n["~standard"].validate=="function"}function et(n,e,t){const s=n["~standard"].validate(e);if(s instanceof Promise)throw new Error(`Prop "${t}" uses an async schema. ForgeFrame only supports synchronous schema validation. Please use a synchronous schema or remove async operations (like database lookups) from your schema definition.`);if(s.issues){const i=s.issues.map(r=>`${tt(r.path,t)}: ${r.message}`);throw new Error(`Validation failed: ${i.join("; ")}`)}return s.value}function tt(n,e){if(!n||n.length===0)return e;const t=n.map(s=>{if(typeof s=="object"&&s!==null){if("key"in s&&s.key!==void 0)return String(s.key);if("index"in s&&typeof s.index=="number")return String(s.index)}return String(s)});return`${e}.${t.join(".")}`}function _e(n,e,t){const s={...O,...e},i={};for(const[r,o]of Object.entries(s)){const c=w(o)?{schema:o}:o;let l;const d=c.alias,b=r in n,P=d&&d in n;if(b)l=n[r];else if(P)l=n[d];else if(c.value)l=c.value(t);else if(c.default!==void 0)l=typeof c.default=="function"?c.default(t):c.default;else if(c.schema&&w(c.schema)){const R=c.schema["~standard"].validate(void 0);!(R instanceof Promise)&&!R.issues&&(l=R.value)}l!==void 0&&c.decorate&&(l=c.decorate({value:l,props:i})),i[r]=l}return i}function nt(n,e){const t={...O,...e};for(const[s,i]of Object.entries(t)){const r=w(i),o=r?{schema:i}:i;let a=n[s];if(o.required&&a===void 0)throw new Error(`Prop "${s}" is required but was not provided`);if(o.schema&&w(o.schema))(a!==void 0||r)&&(a=et(o.schema,a,s),n[s]=a);else if(a===void 0)continue;o.validate&&o.validate({value:a,props:n})}}function Ee(n,e,t,s){const i={...O,...e},r={};for(const[o,a]of Object.entries(i)){const l=w(a)?{schema:a}:a,d=n[o];if(l.sendToHost===!1||l.sameDomain&&!s)continue;if(l.trustedDomains){const P=l.trustedDomains;if(!$(P,t))continue}let b=d;l.hostDecorate&&d!==void 0&&(b=l.hostDecorate({value:d,props:n})),r[o]=b}return r}function st(n,e){const t=new URLSearchParams,s={...O,...e};for(const[i,r]of Object.entries(s)){const a=w(r)?{schema:r}:r,c=n[i];if(c===void 0||typeof c=="function"||!a.queryParam)continue;const l=typeof a.queryParam=="string"?a.queryParam:i;let d;typeof a.queryParam=="function"?d=a.queryParam({value:c}):typeof c=="object"?d=JSON.stringify(c):d=String(c),t.set(l,d)}return t}function it(n,e){const t=new URLSearchParams,s={...O,...e};for(const[i,r]of Object.entries(s)){const a=w(r)?{schema:r}:r,c=n[i];if(c===void 0||typeof c=="function"||!a.bodyParam)continue;const l=typeof a.bodyParam=="string"?a.bodyParam:i;let d;typeof a.bodyParam=="function"?d=a.bodyParam({value:c}):typeof c=="object"?d=JSON.stringify(c):d=String(c),t.set(l,d)}return t}function be(n,e=""){const t=[];for(const[s,i]of Object.entries(n)){const r=e?`${e}.${s}`:s;if(i!==null&&typeof i=="object"&&!Array.isArray(i))t.push(be(i,r));else{const o=encodeURIComponent(JSON.stringify(i));t.push(`${r}=${o}`)}}return t.filter(Boolean).join("&")}function rt(n){const e={};if(!n)return e;const t=n.split("&");for(const s of t){const[i,r]=s.split("=");if(!i||r===void 0)continue;let o;try{o=JSON.parse(decodeURIComponent(r))}catch{o=decodeURIComponent(r)}const a=i.split(".");let c=e;for(let l=0;l<a.length-1;l++){const d=a[l];(!(d in c)||typeof c[d]!="object")&&(c[d]={}),c=c[d]}c[a[a.length-1]]=o}return e}function ot(n){return typeof n=="object"&&n!==null&&n.__type__==="dotify"&&typeof n.__value__=="string"}function Pe(n,e,t){const s={...O,...e},i={};for(const[r,o]of Object.entries(n)){if(o===void 0)continue;const a=s[r];i[r]=at(o,a,t)}return i}function at(n,e,t){if(typeof n=="function")return t.serialize(n);const s=e?.serialization??v.JSON;if(s===v.BASE64&&typeof n=="object"){const i=JSON.stringify(n);return{__type__:"base64",__value__:btoa(encodeURIComponent(i))}}return s===v.DOTIFY&&typeof n=="object"&&n!==null&&!Array.isArray(n)?{__type__:"dotify",__value__:be(n)}:G(n,t)}function Oe(n,e,t,s,i,r){const o={...O,...e},a={};for(const[c,l]of Object.entries(n)){const d=o[c];a[c]=ct(l,d,t,s,i,r)}return a}function ct(n,e,t,s,i,r){if(lt(n))try{const o=decodeURIComponent(atob(n.__value__));return JSON.parse(o)}catch{return n}if(ot(n))try{return rt(n.__value__)}catch{return n}return Y(n,s,i,r)}function lt(n){return typeof n=="object"&&n!==null&&n.__type__==="base64"&&typeof n.__value__=="string"}function S(n,e="100%"){return n===void 0?e:typeof n=="number"?`${n}px`:n}function j(n,e){if(n===void 0)return e;if(typeof n=="number")return n;const t=parseInt(n,10);return isNaN(t)?e:t}function ut(n){try{n.src="about:blank",n.parentNode?.removeChild(n)}catch{}}function ht(n,e){pt(n,e)}function dt(n){n.style.display="",n.style.visibility="visible"}function Se(n){n.style.display="none",n.style.visibility="hidden"}function ft(n){try{n.focus(),n.contentWindow?.focus()}catch{}}function pt(n,e){e.width!==void 0&&(n.style.width=S(e.width)),e.height!==void 0&&(n.style.height=S(e.height))}class ne extends Error{constructor(e="Popup blocked by browser"){super(e),this.name="PopupOpenError"}}function mt(n){const{url:e,name:t,dimensions:s}=n,i=j(s.width,500),r=j(s.height,500),o=Math.floor(window.screenX+(window.outerWidth-i)/2),a=Math.floor(window.screenY+(window.outerHeight-r)/2),c=[`width=${i}`,`height=${r}`,`left=${o}`,`top=${a}`,"menubar=no","toolbar=no","location=yes","status=no","resizable=yes","scrollbars=yes"].join(","),l=window.open(e,t,c);if(!l||wt(l))throw new ne;return l}function gt(n){try{n.closed||n.close()}catch{}}function yt(n){try{n.closed||n.focus()}catch{}}function wt(n){if(!n)return!0;try{return!!(n.closed||n.innerHeight===0||n.innerWidth===0)}catch{return!0}}function _t(n,e,t={}){const{initialInterval:s=100,maxInterval:i=2e3,multiplier:r=1.5}=t;let o=s,a,c=!1;const l=()=>{try{e()}catch(b){console.error("Error in popup close callback:",b)}},d=()=>{if(!c){try{if(n.closed){l();return}}catch{l();return}o=Math.min(o*r,i),a=setTimeout(d,o)}};return a=setTimeout(d,o),()=>{c=!0,clearTimeout(a)}}function Et(n,e){try{const t=j(e.width,n.outerWidth),s=j(e.height,n.outerHeight);n.resizeTo(t,s)}catch{}}function bt(n){const{doc:e,dimensions:t,uid:s,tag:i}=n,r=e.createElement("div");return r.id=`forgeframe-container-${s}`,r.setAttribute("data-forgeframe-tag",i),Object.assign(r.style,{display:"inline-block",position:"relative",width:S(t.width),height:S(t.height),overflow:"hidden"}),r}function Pt(n){const{doc:e,dimensions:t,cspNonce:s}=n,i=e.createElement("div");Object.assign(i.style,{display:"flex",alignItems:"center",justifyContent:"center",width:S(t.width),height:S(t.height),backgroundColor:"#f5f5f5",position:"absolute",top:"0",left:"0",zIndex:"100"});const r=e.createElement("div");Object.assign(r.style,{width:"40px",height:"40px",border:"3px solid #e0e0e0",borderTopColor:"#3498db",borderRadius:"50%",animation:"forgeframe-spin 1s linear infinite"});const o=e.createElement("style");return s&&o.setAttribute("nonce",s),o.textContent=`
|
|
1
|
+
(function(u,f){typeof exports=="object"&&typeof module<"u"?f(exports):typeof define=="function"&&define.amd?define(["exports"],f):(u=typeof globalThis<"u"?globalThis:u||self,f(u.ForgeFrame={}))})(this,(function(u){"use strict";const f={IFRAME:"iframe",POPUP:"popup"},m={RENDER:"render",RENDERED:"rendered",PRERENDER:"prerender",PRERENDERED:"prerendered",DISPLAY:"display",ERROR:"error",CLOSE:"close",DESTROY:"destroy",PROPS:"props",RESIZE:"resize",FOCUS:"focus"},I={JSON:"json",BASE64:"base64",DOTIFY:"dotify"},U={REQUEST:"request",RESPONSE:"response"},h={INIT:"forgeframe_init",PROPS:"forgeframe_props",CLOSE:"forgeframe_close",RESIZE:"forgeframe_resize",FOCUS:"forgeframe_focus",SHOW:"forgeframe_show",HIDE:"forgeframe_hide",ERROR:"forgeframe_error",EXPORT:"forgeframe_export",CALL:"forgeframe_call",CONSUMER_EXPORT:"forgeframe_consumer_export",GET_SIBLINGS:"forgeframe_get_siblings"},H="__forgeframe__",Y=(()=>{if("0.0.5".trim().length===0)throw new Error("ForgeFrame VERSION injection is missing. Configure __FORGEFRAME_VERSION__ in build/test tooling.");return"0.0.5"})();class he{listeners=new Map;on(e,t){return this.listeners.has(e)||this.listeners.set(e,new Set),this.listeners.get(e).add(t),()=>this.off(e,t)}once(e,t){const n=i=>(this.off(e,n),t(i));return this.on(e,n)}emit(e,t){const n=this.listeners.get(e);if(n)for(const i of n)try{const r=i(t);r&&typeof r=="object"&&"catch"in r&&typeof r.catch=="function"&&r.catch(o=>{console.error(`Error in async event handler for "${e}":`,o)})}catch(r){console.error(`Error in event handler for "${e}":`,r)}}off(e,t){if(!t){this.listeners.delete(e);return}const n=this.listeners.get(e);n&&(n.delete(t),n.size===0&&this.listeners.delete(e))}removeAllListeners(){this.listeners.clear()}listenerCount(e){return this.listeners.get(e)?.size??0}}function We(){const s=Date.now().toString(36),e=Math.random().toString(36).slice(2,11);return`${s}_${e}`}function $(){return Math.random().toString(36).slice(2,11)}class je{tasks=[];cleaned=!1;register(e){if(this.cleaned){Promise.resolve().then(()=>e()).catch(t=>{console.error("Error in cleanup task:",t)});return}this.tasks.push(e)}async cleanup(){if(this.cleaned)return;this.cleaned=!0;const e=this.tasks.reverse();this.tasks=[];for(const t of e)try{await t()}catch(n){console.error("Error in cleanup task:",n)}}isCleaned(){return this.cleaned}reset(){this.tasks=[],this.cleaned=!1}}function de(){let s,e;return{promise:new Promise((n,i)=>{s=n,e=i}),resolve:s,reject:e}}function Be(s,e,t="Operation timed out"){return new Promise((n,i)=>{const r=setTimeout(()=>{i(new Error(`${t} (${e}ms)`))},e);s.then(o=>{clearTimeout(r),n(o)}).catch(o=>{clearTimeout(r),i(o)})})}const Z="forgeframe:";function G(s){return Z+JSON.stringify(s)}function Ve(s){if(typeof s!="string"||!s.startsWith(Z))return null;try{const e=s.slice(Z.length),t=JSON.parse(e);return!t.id||!t.type||!t.name||!t.source?null:t}catch{return null}}function fe(s,e,t,n){return{id:s,type:U.REQUEST,name:e,data:t,source:n}}function qe(s,e,t,n){return{id:s,type:U.RESPONSE,name:"response",data:e,source:t,error:n?{message:n.message,stack:n.stack}:void 0}}function Je(s){return s.replace(/[.*+?^${}()|[\]\\]/g,"\\$&")}function Xe(s){if(!s.includes("*"))return null;const e=s.split("*").map(t=>Je(t)).join(".*");return new RegExp(`^${e}$`)}function Ke(s,e){return s.global||s.sticky?new RegExp(s.source,s.flags.replace(/[gy]/g,"")).test(e):s.test(e)}class pe{constructor(e,t=window,n=window.location.origin,i){this.uid=e,this.win=t,this.domain=n,this.allowedOrigins.add(n),i&&this.addTrustedDomain(i),this.setupListener()}pending=new Map;handlers=new Map;listener=null;destroyed=!1;allowedOrigins=new Set;allowedOriginPatterns=[];wildcardPatternRegistry=new Map;sourceUidRegistry=new WeakMap;addTrustedDomain(e){if(Array.isArray(e))for(const t of e)this.addTrustedDomain(t);else if(e instanceof RegExp)this.allowedOriginPatterns.push(e);else{const t=Xe(e);t?this.wildcardPatternRegistry.has(e)||(this.wildcardPatternRegistry.set(e,t),this.allowedOriginPatterns.push(t)):this.allowedOrigins.add(e)}}removeTrustedDomain(e){if(Array.isArray(e))for(const t of e)this.removeTrustedDomain(t);else if(e instanceof RegExp)this.allowedOriginPatterns=this.allowedOriginPatterns.filter(t=>t!==e);else{const t=this.wildcardPatternRegistry.get(e);t&&(this.allowedOriginPatterns=this.allowedOriginPatterns.filter(n=>n!==t),this.wildcardPatternRegistry.delete(e)),this.allowedOrigins.delete(e)}}isOriginTrusted(e){if(this.allowedOrigins.has(e))return!0;for(const t of this.allowedOriginPatterns)if(Ke(t,e))return!0;return!1}resolveVerifiedSource(e,t,n){const i=e,r=this.sourceUidRegistry.get(i);if(r)return{uid:r,domain:t};const o=n&&typeof n.uid=="string"&&n.uid.length>0?n.uid:$();return this.sourceUidRegistry.set(i,o),{uid:o,domain:t}}async send(e,t,n,i,r=1e4){if(this.destroyed)throw new Error("Messenger has been destroyed");const o=$(),a=fe(o,n,i,{uid:this.uid,domain:this.domain}),c=de(),l=setTimeout(()=>{this.pending.delete(o),c.reject(new Error(`Message "${n}" timed out after ${r}ms`))},r);this.pending.set(o,{deferred:c,timeout:l});try{e.postMessage(G(a),t)}catch(p){throw this.pending.delete(o),clearTimeout(l),p}return c.promise}post(e,t,n,i){if(this.destroyed)throw new Error("Messenger has been destroyed");const r=$(),o=fe(r,n,i,{uid:this.uid,domain:this.domain});e.postMessage(G(o),t)}on(e,t){return this.handlers.set(e,t),()=>this.handlers.delete(e)}setupListener(){this.listener=e=>{if(e.source===this.win||!this.isOriginTrusted(e.origin))return;const t=Ve(e.data);if(!t)return;const n=e.source;!n||typeof n.postMessage!="function"||this.handleMessage(t,n,e.origin)},this.win.addEventListener("message",this.listener)}async handleMessage(e,t,n){if(e.type===U.RESPONSE){const i=this.pending.get(e.id);if(i)if(this.pending.delete(e.id),clearTimeout(i.timeout),e.error){const r=new Error(e.error.message);r.stack=e.error.stack,i.deferred.reject(r)}else i.deferred.resolve(e.data);return}if(e.type===U.REQUEST){const i=this.handlers.get(e.name);if(!i)return;let r,o;try{const c=this.resolveVerifiedSource(t,n,e.source);r=await i(e.data,c)}catch(c){o=c instanceof Error?c:new Error(String(c))}const a=qe(e.id,r,{uid:this.uid,domain:this.domain},o);try{t.postMessage(G(a),n)}catch{}}}destroy(){if(!this.destroyed){this.destroyed=!0,this.listener&&(this.win.removeEventListener("message",this.listener),this.listener=null);for(const e of this.pending.values())clearTimeout(e.timeout),e.deferred.reject(new Error("Messenger destroyed"));this.pending.clear(),this.handlers.clear()}}isDestroyed(){return this.destroyed}}const me=500;class Q{constructor(e){this.messenger=e,this.setupCallHandler()}localFunctions=new Map;remoteFunctions=new Map;currentBatchIds=new Set;serialize(e,t){if(this.localFunctions.size>=me){const i=this.localFunctions.keys().next().value;i&&this.localFunctions.delete(i)}const n=$();return this.localFunctions.set(n,e),this.currentBatchIds.add(n),{__type__:"function",__id__:n,__name__:t||e.name||"anonymous"}}deserialize(e,t,n){const i=`${e.__id__}`,r=this.remoteFunctions.get(i);if(r)return r;if(this.remoteFunctions.size>=me){const a=this.remoteFunctions.keys().next().value;a&&this.remoteFunctions.delete(a)}const o=async(...a)=>this.messenger.send(t,n,h.CALL,{id:e.__id__,args:a});return Object.defineProperty(o,"name",{value:e.__name__,configurable:!0}),this.remoteFunctions.set(i,o),o}static isFunctionRef(e){return typeof e=="object"&&e!==null&&e.__type__==="function"&&typeof e.__id__=="string"}setupCallHandler(){this.messenger.on(h.CALL,async({id:e,args:t})=>{const n=this.localFunctions.get(e);if(!n)throw new Error(`Function with id "${e}" not found`);return n(...t)})}removeLocal(e){this.localFunctions.delete(e)}startBatch(){this.currentBatchIds.clear()}finishBatch(e=!1){if(e){this.currentBatchIds.clear();return}for(const t of this.localFunctions.keys())this.currentBatchIds.has(t)||this.localFunctions.delete(t);this.currentBatchIds.clear()}clearRemote(){this.remoteFunctions.clear()}get localFunctionCount(){return this.localFunctions.size}get remoteFunctionCount(){return this.remoteFunctions.size}destroy(){this.localFunctions.clear(),this.remoteFunctions.clear(),this.currentBatchIds.clear()}}function ee(s,e,t=new WeakSet){if(typeof s=="function")return e.serialize(s);if(Array.isArray(s)){if(t.has(s))throw new Error("Circular reference detected in props - arrays cannot contain circular references");t.add(s);try{return s.map(n=>ee(n,e,t))}finally{t.delete(s)}}if(typeof s=="object"&&s!==null){if(t.has(s))throw new Error("Circular reference detected in props - objects cannot contain circular references");t.add(s);try{const n={};for(const[i,r]of Object.entries(s))n[i]=ee(r,e,t);return n}finally{t.delete(s)}}return s}function te(s,e,t,n,i=new WeakSet){if(Q.isFunctionRef(s))return e.deserialize(s,t,n);if(Array.isArray(s)){if(i.has(s))throw new Error("Circular reference detected in serialized props");i.add(s);try{return s.map(r=>te(r,e,t,n,i))}finally{i.delete(s)}}if(typeof s=="object"&&s!==null){if(i.has(s))throw new Error("Circular reference detected in serialized props");i.add(s);try{const r={};for(const[o,a]of Object.entries(s))r[o]=te(a,e,t,n,i);return r}finally{i.delete(s)}}return s}const C=new Map,Ye=200;function Ze(s){return s.replace(/[.*+?^${}()|[\]\\]/g,"\\$&")}function Ge(s){if(!s.includes("*"))return null;const e=C.get(s);if(e)return e;const t=s.split("*").map(i=>Ze(i)).join(".*"),n=new RegExp(`^${t}$`);if(C.size>=Ye){const i=C.keys().next().value;i&&C.delete(i)}return C.set(s,n),n}function Qe(s,e){return s.global||s.sticky?new RegExp(s.source,s.flags.replace(/[gy]/g,"")).test(e):s.test(e)}function L(s=window){try{return s.location.origin}catch{return""}}function ge(s,e=window){try{return s.location.origin===e.location.origin}catch{return!1}}function S(s,e){if(typeof s=="string"){if(s==="*")return!0;const t=Ge(s);return t?t.test(e):s===e}return s instanceof RegExp?Qe(s,e):Array.isArray(s)?s.some(t=>S(t,e)):!1}function k(s){if(!s)return!0;try{return s.closed}catch{return!0}}function et(s=window){try{return s.opener}catch{return null}}function tt(s=window){try{const e=s.parent;return e&&e!==s?e:null}catch{return null}}function st(s=window){try{return s.parent!==s}catch{return!0}}function nt(s=window){try{return s.opener!==null&&s.opener!==void 0}catch{return!1}}const ye=32*1024;function it(s){const e=rt(s);return`${H}${e}`}function we(s){if(!s||!s.startsWith(H))return null;const e=s.slice(H.length);return ot(e)}function se(s=window){try{return s.name.startsWith(H)}catch{return!1}}function ne(s,e=window){return we(e.name)?.tag===s}function rt(s){try{const e=JSON.stringify(s),t=btoa(encodeURIComponent(e)),n=new Blob([t]).size;if(n>ye)throw new Error(`Payload size (${Math.round(n/1024)}KB) exceeds maximum allowed size (${ye/1024}KB). Consider reducing the amount of data passed via props.`);return t}catch(e){throw e instanceof Error&&e.message.includes("Payload size")?e:new Error(`Failed to encode payload: ${e}`)}}function ot(s){try{const e=decodeURIComponent(atob(s));return JSON.parse(e)}catch{return null}}function at(s){return{uid:s.uid,tag:s.tag,version:Y,context:s.context,consumerDomain:s.consumerDomain,props:s.props,exports:s.exports,children:s.children}}function ct(s=window){return we(s.name)}const _e=100,_=new Map;function lt(){const s=[];for(const[e,t]of _.entries())k(t)&&s.push(e);for(const e of s)_.delete(e)}function ut(s,e){if(_.size>=_e&<(),_.size>=_e){const t=_.keys().next().value;t&&_.delete(t)}_.set(s,e)}function ht(s){_.delete(s)}function dt(s,e){return s.global||s.sticky?new RegExp(s.source,s.flags.replace(/[gy]/g,"")).test(e):s.test(e)}class g{_optional=!1;_nullable=!1;_default;"~standard"={version:1,vendor:"forgeframe",validate:e=>e===null?this._nullable?{value:null}:{issues:[{message:"Expected a value, got null"}]}:e===void 0?this._default!==void 0?{value:typeof this._default=="function"?this._default():this._default}:this._optional?{value:void 0}:{issues:[{message:"Required"}]}:this._validate(e)};optional(){const e=this._clone();return e._optional=!0,e}nullable(){const e=this._clone();return e._nullable=!0,e}default(e){const t=this._clone();return t._default=e,t}}class A extends g{_minLength;_maxLength;_pattern;_patternMessage;_trim=!1;_validate(e){if(typeof e!="string")return{issues:[{message:`Expected string, got ${typeof e}`}]};const t=this._trim?e.trim():e;return this._minLength!==void 0&&t.length<this._minLength?{issues:[{message:`String must be at least ${this._minLength} characters`}]}:this._maxLength!==void 0&&t.length>this._maxLength?{issues:[{message:`String must be at most ${this._maxLength} characters`}]}:this._pattern&&!dt(this._pattern,t)?{issues:[{message:this._patternMessage||`String must match pattern ${this._pattern}`}]}:{value:t}}_clone(){const e=new A;return e._optional=this._optional,e._nullable=this._nullable,e._default=this._default,e._minLength=this._minLength,e._maxLength=this._maxLength,e._pattern=this._pattern,e._patternMessage=this._patternMessage,e._trim=this._trim,e}min(e){const t=this._clone();return t._minLength=e,t}max(e){const t=this._clone();return t._maxLength=e,t}length(e){const t=this._clone();return t._minLength=e,t._maxLength=e,t}pattern(e,t){const n=this._clone();return n._pattern=e,n._patternMessage=t,n}email(){return this.pattern(/^[^\s@]+@[^\s@]+\.[^\s@]+$/,"Invalid email address")}url(){return this.pattern(/^https?:\/\/.+/,"Invalid URL")}uuid(){return this.pattern(/^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i,"Invalid UUID")}trim(){const e=this._clone();return e._trim=!0,e}nonempty(){const e=this._clone();return e._minLength=1,e}}class M extends g{_min;_max;_int=!1;_validate(e){return typeof e!="number"||Number.isNaN(e)?{issues:[{message:`Expected number, got ${typeof e}`}]}:this._int&&!Number.isInteger(e)?{issues:[{message:"Expected integer"}]}:this._min!==void 0&&e<this._min?{issues:[{message:`Number must be >= ${this._min}`}]}:this._max!==void 0&&e>this._max?{issues:[{message:`Number must be <= ${this._max}`}]}:{value:e}}_clone(){const e=new M;return e._optional=this._optional,e._nullable=this._nullable,e._default=this._default,e._min=this._min,e._max=this._max,e._int=this._int,e}min(e){const t=this._clone();return t._min=e,t}max(e){const t=this._clone();return t._max=e,t}int(){const e=this._clone();return e._int=!0,e}positive(){const e=this._clone();return e._min=Number.MIN_VALUE,e}nonnegative(){const e=this._clone();return e._min=0,e}negative(){const e=this._clone();return e._max=-Number.MIN_VALUE,e}}class z extends g{_validate(e){return typeof e!="boolean"?{issues:[{message:`Expected boolean, got ${typeof e}`}]}:{value:e}}_clone(){const e=new z;return e._optional=this._optional,e._nullable=this._nullable,e._default=this._default,e}}class W extends g{_validate(e){return typeof e!="function"?{issues:[{message:`Expected function, got ${typeof e}`}]}:{value:e}}_clone(){const e=new W;return e._optional=this._optional,e._nullable=this._nullable,e._default=this._default,e}}class D extends g{_itemSchema;_minLength;_maxLength;_validate(e){if(!Array.isArray(e))return{issues:[{message:`Expected array, got ${typeof e}`}]};if(this._minLength!==void 0&&e.length<this._minLength)return{issues:[{message:`Array must have at least ${this._minLength} items`}]};if(this._maxLength!==void 0&&e.length>this._maxLength)return{issues:[{message:`Array must have at most ${this._maxLength} items`}]};if(this._itemSchema){const t=[];for(let n=0;n<e.length;n++){const i=this._itemSchema["~standard"].validate(e[n]);if(i instanceof Promise)throw new Error("Async schema validation is not supported. Use synchronous schemas.");if(i.issues)return{issues:i.issues.map(r=>({...r,path:[n,...r.path||[]]}))};t.push(i.value)}return{value:t}}return{value:e}}_clone(){const e=new D;return e._optional=this._optional,e._nullable=this._nullable,e._default=this._default,e._itemSchema=this._itemSchema,e._minLength=this._minLength,e._maxLength=this._maxLength,e}of(e){const t=new D;return t._optional=this._optional,t._nullable=this._nullable,t._itemSchema=e,t._minLength=this._minLength,t._maxLength=this._maxLength,t}min(e){const t=this._clone();return t._minLength=e,t}max(e){const t=this._clone();return t._maxLength=e,t}nonempty(){return this.min(1)}}class x extends g{_shape;_strict=!1;_validate(e){if(typeof e!="object"||e===null||Array.isArray(e))return{issues:[{message:`Expected object, got ${Array.isArray(e)?"array":typeof e}`}]};const t=e,n={};if(this._shape){if(this._strict){const i=new Set(Object.keys(this._shape));for(const r of Object.keys(t))if(!i.has(r))return{issues:[{message:`Unknown key: ${r}`,path:[r]}]}}for(const[i,r]of Object.entries(this._shape)){const o=r["~standard"].validate(t[i]);if(o instanceof Promise)throw new Error("Async schema validation is not supported. Use synchronous schemas.");if(o.issues)return{issues:o.issues.map(a=>({...a,path:[i,...a.path||[]]}))};n[i]=o.value}if(!this._strict)for(const i of Object.keys(t))i in this._shape||(n[i]=t[i]);return{value:n}}return{value:e}}_clone(){const e=new x;return e._optional=this._optional,e._nullable=this._nullable,e._default=this._default,e._shape=this._shape,e._strict=this._strict,e}shape(e){const t=new x;return t._optional=this._optional,t._nullable=this._nullable,t._shape=e,t._strict=this._strict,t}strict(){const e=this._clone();return e._strict=!0,e}}class j extends g{_value;constructor(e){super(),this._value=e}_validate(e){return e!==this._value?{issues:[{message:`Expected ${JSON.stringify(this._value)}, got ${JSON.stringify(e)}`}]}:{value:e}}_clone(){const e=new j(this._value);return e._optional=this._optional,e._nullable=this._nullable,e._default=this._default,e}}class B extends g{_values;_valueSet;constructor(e){super(),this._values=e,this._valueSet=new Set(e)}_validate(e){return this._valueSet.has(e)?{value:e}:{issues:[{message:`Expected one of [${this._values.map(t=>JSON.stringify(t)).join(", ")}], got ${JSON.stringify(e)}`}]}}_clone(){const e=new B(this._values);return e._optional=this._optional,e._nullable=this._nullable,e._default=this._default,e}}class V extends g{constructor(){super(),this._nullable=!0}_validate(e){return{value:e}}_clone(){const e=new V;return e._optional=this._optional,e._nullable=this._nullable,e._default=this._default,e}}const d={string:()=>new A,number:()=>new M,boolean:()=>new z,function:()=>new W,array:()=>new D,object:()=>new x,literal:s=>new j(s),enum:s=>new B(s),any:()=>new V},P={uid:{schema:d.string().optional(),sendToHost:!0},tag:{schema:d.string().optional(),sendToHost:!0},dimensions:{schema:d.object().default(()=>({width:"100%",height:"100%"})),sendToHost:!1},timeout:{schema:d.number().default(1e4),sendToHost:!1},cspNonce:{schema:d.string().optional(),sendToHost:!0},onDisplay:{schema:d.function().optional(),sendToHost:!1},onRendered:{schema:d.function().optional(),sendToHost:!1},onRender:{schema:d.function().optional(),sendToHost:!1},onPrerendered:{schema:d.function().optional(),sendToHost:!1},onPrerender:{schema:d.function().optional(),sendToHost:!1},onClose:{schema:d.function().optional(),sendToHost:!1},onDestroy:{schema:d.function().optional(),sendToHost:!1},onResize:{schema:d.function().optional(),sendToHost:!1},onFocus:{schema:d.function().optional(),sendToHost:!1},onError:{schema:d.function().optional(),sendToHost:!1},onProps:{schema:d.function().optional(),sendToHost:!1}};function T(s){return typeof s=="object"&&s!==null&&"~standard"in s&&typeof s["~standard"]=="object"&&s["~standard"]!==null&&s["~standard"].version===1&&typeof s["~standard"].vendor=="string"&&typeof s["~standard"].validate=="function"}function ft(s,e,t){const n=s["~standard"].validate(e);if(n instanceof Promise)throw new Error(`Prop "${t}" uses an async schema. ForgeFrame only supports synchronous schema validation. Please use a synchronous schema or remove async operations (like database lookups) from your schema definition.`);if(n.issues){const i=n.issues.map(r=>`${pt(r.path,t)}: ${r.message}`);throw new Error(`Validation failed: ${i.join("; ")}`)}return n.value}function pt(s,e){if(!s||s.length===0)return e;const t=s.map(n=>{if(typeof n=="object"&&n!==null){if("key"in n&&n.key!==void 0)return String(n.key);if("index"in n&&typeof n.index=="number")return String(n.index)}return String(n)});return`${e}.${t.join(".")}`}function N(s){const e=T(s);return{isDirectSchema:e,definition:e?{schema:s}:s}}function Ee(s,e,t){const n={...P,...e},i={};for(const[r,o]of Object.entries(n)){const{definition:a}=N(o);let c;const l=a.alias,p=r in s,b=l&&l in s;if(p)c=s[r];else if(b)c=s[l];else if(a.value)c=a.value(t);else if(a.default!==void 0)c=typeof a.default=="function"?a.default(t):a.default;else if(a.schema&&T(a.schema)){const E=a.schema["~standard"].validate(void 0);!(E instanceof Promise)&&!E.issues&&(c=E.value)}c!==void 0&&a.decorate&&(c=a.decorate({value:c,props:i})),i[r]=c}return i}function Pe(s,e){const t={...P,...e};for(const[n,i]of Object.entries(t)){const{isDirectSchema:r,definition:o}=N(i);let a=s[n];if(o.required&&a===void 0)throw new Error(`Prop "${n}" is required but was not provided`);if(o.schema&&T(o.schema))(a!==void 0||r)&&(a=ft(o.schema,a,n),s[n]=a);else if(a===void 0)continue;o.validate&&o.validate({value:a,props:s})}}function be(s,e,t,n){const i={...P,...e},r={};for(const[o,a]of Object.entries(i)){const{definition:c}=N(a),l=s[o];if(c.sendToHost===!1||c.sameDomain&&!n)continue;if(c.trustedDomains){const b=c.trustedDomains;if(!S(b,t))continue}let p=l;c.hostDecorate&&l!==void 0&&(p=c.hostDecorate({value:l,props:s})),r[o]=p}return r}function mt(s,e){const t=new URLSearchParams,n={...P,...e};for(const[i,r]of Object.entries(n)){const{definition:o}=N(r),a=s[i];if(a===void 0||typeof a=="function"||!o.queryParam)continue;const c=typeof o.queryParam=="string"?o.queryParam:i;let l;typeof o.queryParam=="function"?l=o.queryParam({value:a}):typeof a=="object"?l=JSON.stringify(a):l=String(a),t.set(c,l)}return t}function gt(s,e){const t=new URLSearchParams,n={...P,...e};for(const[i,r]of Object.entries(n)){const{definition:o}=N(r),a=s[i];if(a===void 0||typeof a=="function"||!o.bodyParam)continue;const c=typeof o.bodyParam=="string"?o.bodyParam:i;let l;typeof o.bodyParam=="function"?l=o.bodyParam({value:a}):typeof a=="object"?l=JSON.stringify(a):l=String(a),t.set(c,l)}return t}function Re(s,e=""){const t=[];for(const[n,i]of Object.entries(s)){const r=e?`${e}.${n}`:n;if(i!==null&&typeof i=="object"&&!Array.isArray(i))t.push(Re(i,r));else{const o=encodeURIComponent(JSON.stringify(i));t.push(`${r}=${o}`)}}return t.filter(Boolean).join("&")}function yt(s){const e={};if(!s)return e;const t=s.split("&");for(const n of t){const[i,r]=n.split("=");if(!i||r===void 0)continue;let o;try{o=JSON.parse(decodeURIComponent(r))}catch{o=decodeURIComponent(r)}const a=i.split(".");let c=e;for(let l=0;l<a.length-1;l++){const p=a[l];(!(p in c)||typeof c[p]!="object")&&(c[p]={}),c=c[p]}c[a[a.length-1]]=o}return e}function wt(s){return typeof s=="object"&&s!==null&&s.__type__==="dotify"&&typeof s.__value__=="string"}function _t(s,e,t){const n={...P,...e},i={};for(const[r,o]of Object.entries(s)){if(o===void 0)continue;const a=n[r];i[r]=Et(o,a,t)}return i}function Et(s,e,t){if(typeof s=="function")return t.serialize(s);const n=e?.serialization??I.JSON;if(n===I.BASE64&&typeof s=="object"){const i=JSON.stringify(s);return{__type__:"base64",__value__:btoa(encodeURIComponent(i))}}return n===I.DOTIFY&&typeof s=="object"&&s!==null&&!Array.isArray(s)?{__type__:"dotify",__value__:Re(s)}:ee(s,t)}function Se(s,e,t,n,i,r){const o={...P,...e},a={};for(const[c,l]of Object.entries(s)){const p=o[c];a[c]=Pt(l,p,t,n,i,r)}return a}function Pt(s,e,t,n,i,r){if(bt(s))try{const o=decodeURIComponent(atob(s.__value__));return JSON.parse(o)}catch{return s}if(wt(s))try{return yt(s.__value__)}catch{return s}return te(s,n,i,r)}function bt(s){return typeof s=="object"&&s!==null&&s.__type__==="base64"&&typeof s.__value__=="string"}function O(s,e="100%"){return s===void 0?e:typeof s=="number"?`${s}px`:s}function q(s,e){if(s===void 0)return e;if(typeof s=="number")return s;const t=parseInt(s,10);return isNaN(t)?e:t}function Rt(s){try{s.src="about:blank",s.parentNode?.removeChild(s)}catch{}}function St(s,e){It(s,e)}function Ot(s){s.style.display="",s.style.visibility="visible"}function Oe(s){s.style.display="none",s.style.visibility="hidden"}function vt(s){try{s.focus(),s.contentWindow?.focus()}catch{}}function It(s,e){e.width!==void 0&&(s.style.width=O(e.width)),e.height!==void 0&&(s.style.height=O(e.height))}class ie extends Error{constructor(e="Popup blocked by browser"){super(e),this.name="PopupOpenError"}}function Ct(s){const{url:e,name:t,dimensions:n}=s,i=q(n.width,500),r=q(n.height,500),o=Math.floor(window.screenX+(window.outerWidth-i)/2),a=Math.floor(window.screenY+(window.outerHeight-r)/2),c=[`width=${i}`,`height=${r}`,`left=${o}`,`top=${a}`,"menubar=no","toolbar=no","location=yes","status=no","resizable=yes","scrollbars=yes"].join(","),l=window.open(e,t,c);if(!l||Tt(l))throw new ie;return l}function Dt(s){try{s.closed||s.close()}catch{}}function xt(s){try{s.closed||s.focus()}catch{}}function Tt(s){if(!s)return!0;try{return!!(s.closed||s.innerHeight===0||s.innerWidth===0)}catch{return!0}}function Nt(s,e,t={}){const{initialInterval:n=100,maxInterval:i=2e3,multiplier:r=1.5}=t;let o=n,a,c=!1;const l=()=>{try{e()}catch(b){console.error("Error in popup close callback:",b)}},p=()=>{if(!c){try{if(s.closed){l();return}}catch{l();return}o=Math.min(o*r,i),a=setTimeout(p,o)}};return a=setTimeout(p,o),()=>{c=!0,clearTimeout(a)}}function Ft(s,e){try{const t=q(e.width,s.outerWidth),n=q(e.height,s.outerHeight);s.resizeTo(t,n)}catch{}}const ve="forgeframe-spinner-style";function Ut(s,e){const t=s.getElementById(ve);if(t){const i=t.getAttribute("nonce");if(!e||i===e)return;t.remove()}const n=s.createElement("style");n.id=ve,e&&n.setAttribute("nonce",e),n.textContent=`
|
|
2
2
|
@keyframes forgeframe-spin {
|
|
3
3
|
to { transform: rotate(360deg); }
|
|
4
4
|
}
|
|
5
|
-
`,i.appendChild(o),i.appendChild(r),i}function Ot(n,e=200){return new Promise(t=>{n.style.opacity="0",n.style.transition=`opacity ${e}ms ease-in`,n.offsetHeight,n.style.opacity="1",setTimeout(t,e)})}function St(n,e=200){return new Promise(t=>{n.style.transition=`opacity ${e}ms ease-out`,n.style.opacity="0",setTimeout(t,e)})}async function Rt(n,e,t){e&&(await St(e,150),e.remove()),t.style.display="",t.style.visibility="visible",t.style.opacity="0",await Ot(t,150)}class se{event;state={};exports;consumerExports;_uid;get uid(){return this._uid}options;props;context;messenger;bridge;cleanup;hostWindow=null;openedHostDomain=null;dynamicUrlTrustedOrigin=null;iframe=null;container=null;prerenderElement=null;initPromise=null;rendered=!1;destroyed=!1;constructor(e,t={}){this._uid=He(),this.options=this.normalizeOptions(e),this.context=this.options.defaultContext,this.event=new le,this.cleanup=new Ae;const s=this.createPropContext();this.props=_e(t,this.options.props,s);const i=this.buildTrustedDomains();this.messenger=new de(this.uid,window,Q(),i),this.bridge=new Z(this.messenger),this.setupMessageHandlers(),this.setupCleanup()}buildTrustedDomains(){const e=[],t=this.resolveUrlOrigin(this.resolveUrl());if(t&&(e.push(t),this.dynamicUrlTrustedOrigin=t),this.options.domain){if(typeof this.options.domain=="string")e.push(this.options.domain);else if(Array.isArray(this.options.domain))e.push(...this.options.domain);else if(this.options.domain instanceof RegExp)return this.options.domain}return e.length>0?e:void 0}async render(e,t){if(this.destroyed)throw new Error("Component has been destroyed");if(this.rendered)throw new Error("Component has already been rendered");this.context=t??this.options.defaultContext,this.checkEligibility(),nt(this.props,this.options.props),this.options.validate?.({props:this.props}),this.container=this.resolveContainer(e),this.event.emit(m.PRERENDER),this.callPropCallback("onPrerender"),await this.prerender(),this.event.emit(m.PRERENDERED),this.callPropCallback("onPrerendered"),this.event.emit(m.RENDER),this.callPropCallback("onRender"),await this.open(),await this.waitForHost(),this.context===p.IFRAME&&this.iframe&&this.prerenderElement&&(await Rt(this.container,this.prerenderElement,this.iframe),this.prerenderElement=null),this.rendered=!0,this.event.emit(m.RENDERED),this.callPropCallback("onRendered"),this.event.emit(m.DISPLAY),this.callPropCallback("onDisplay")}async renderTo(e,t,s){return this.render(t,s)}async close(){this.destroyed||(this.event.emit(m.CLOSE),await this.destroy())}async focus(){this.context===p.IFRAME&&this.iframe?ft(this.iframe):this.context===p.POPUP&&this.hostWindow&&yt(this.hostWindow),this.event.emit(m.FOCUS),this.callPropCallback("onFocus")}async resize(e){this.context===p.IFRAME&&this.iframe?ht(this.iframe,e):this.context===p.POPUP&&this.hostWindow&&Et(this.hostWindow,e),this.event.emit(m.RESIZE,e),this.callPropCallback("onResize",e)}async show(){this.context===p.IFRAME&&this.iframe&&dt(this.iframe)}async hide(){this.context===p.IFRAME&&this.iframe&&Se(this.iframe)}async updateProps(e){const t=this.createPropContext(),s=_e({...this.props,...e},this.options.props,t);this.options.validate?.({props:s});const i=this.resolveUrl(s),r=this.resolveUrlOrigin(i);if(this.rendered&&this.openedHostDomain&&r&&r!==this.openedHostDomain)throw new Error(`Cannot change component URL origin after render (from "${this.openedHostDomain}" to "${r}")`);if(this.props=s,this.rendered||this.syncTrustedDomainForUrl(i),this.hostWindow&&!me(this.hostWindow)){const o=this.openedHostDomain??this.getHostDomain(),a=Ee(s,this.options.props,o,pe(this.hostWindow)),c=Pe(a,this.options.props,this.bridge);await this.messenger.send(this.hostWindow,o,h.PROPS,c)}this.event.emit(m.PROPS,this.props),this.callPropCallback("onProps",this.props)}clone(){return new se(this.options,this.props)}isEligible(){return this.options.eligible?this.options.eligible({props:this.props}).eligible:!0}normalizeOptions(e){return{...e,props:e.props??{},defaultContext:e.defaultContext??p.IFRAME,dimensions:e.dimensions??{width:"100%",height:"100%"},timeout:e.timeout??1e4,children:e.children}}resolveUrl(e=this.props){return typeof this.options.url=="function"?this.options.url(e):this.options.url}resolveDimensions(){return typeof this.options.dimensions=="function"?this.options.dimensions(this.props):this.options.dimensions}resolveUrlOrigin(e){try{return new URL(e,window.location.origin).origin}catch{return null}}isExplicitDomainTrust(e){return!this.options.domain||this.options.domain instanceof RegExp?!1:typeof this.options.domain=="string"?this.options.domain===e:this.options.domain.includes(e)}syncTrustedDomainForUrl(e){const t=this.resolveUrlOrigin(e);if(!t)return;const s=this.dynamicUrlTrustedOrigin;s&&s!==t&&!this.isExplicitDomainTrust(s)&&this.messenger.removeTrustedDomain(s),this.messenger.addTrustedDomain(t),this.dynamicUrlTrustedOrigin=t}createPropContext(){return{props:this.props,state:this.state,close:()=>this.close(),focus:()=>this.focus(),onError:e=>this.handleError(e),container:this.container,uid:this.uid,tag:this.options.tag}}resolveContainer(e){if(!e)throw new Error("Container is required for rendering");if(typeof e=="string"){const t=document.querySelector(e);if(!t)throw new Error(`Container "${e}" not found`);return t}return e}checkEligibility(){if(!this.options.eligible)return;const e=this.options.eligible({props:this.props});if(!e.eligible)throw new Error(`Component not eligible: ${e.reason??"Unknown reason"}`)}async prerender(){if(!this.container)return;const e=this.options.prerenderTemplate??Pt,t=this.options.containerTemplate??bt,s=this.resolveDimensions(),i=this.props.cspNonce;if(this.context===p.IFRAME){const c=this.buildWindowName();this.iframe=this.createIframeElement(c),Se(this.iframe)}const r={uid:this.uid,tag:this.options.tag,context:this.context,dimensions:s,props:this.props,doc:document,container:this.container,frame:this.iframe,prerenderFrame:null,close:()=>this.close(),focus:()=>this.focus(),cspNonce:i};this.prerenderElement=e(r);const o={uid:this.uid,tag:this.options.tag,context:this.context,dimensions:s,props:this.props,doc:document,container:this.container,frame:this.iframe,prerenderFrame:this.prerenderElement,close:()=>this.close(),focus:()=>this.focus(),cspNonce:i},a=t(o);a&&(this.container.appendChild(a),this.container=a),this.prerenderElement&&!this.prerenderElement.parentNode&&this.container.appendChild(this.prerenderElement),this.iframe&&!this.iframe.parentNode&&this.container.appendChild(this.iframe)}createIframeElement(e){const t=document.createElement("iframe"),s=this.resolveDimensions(),i=typeof this.options.attributes=="function"?this.options.attributes(this.props):this.options.attributes??{},r=typeof this.options.style=="function"?this.options.style(this.props):this.options.style??{};t.name=e,t.setAttribute("frameborder","0"),t.setAttribute("allowtransparency","true"),t.setAttribute("scrolling","auto"),s.width!==void 0&&(t.style.width=typeof s.width=="number"?`${s.width}px`:s.width),s.height!==void 0&&(t.style.height=typeof s.height=="number"?`${s.height}px`:s.height);for(const[o,a]of Object.entries(i))a!==void 0&&(typeof a=="boolean"?a&&t.setAttribute(o,""):t.setAttribute(o,a));for(const[o,a]of Object.entries(r)){if(a===void 0)continue;const c=typeof a=="number"?`${a}px`:a;t.style.setProperty(o.replace(/([A-Z])/g,"-$1").toLowerCase(),String(c))}return i.sandbox||t.setAttribute("sandbox","allow-scripts allow-same-origin allow-forms allow-popups allow-popups-to-escape-sandbox"),t}async open(){const e=this.resolveUrl();this.syncTrustedDomainForUrl(e),this.openedHostDomain=this.resolveUrlOrigin(e);const t=this.buildUrl(e),s=this.buildBodyParams(),i=s.toString().length>0;if(this.context===p.IFRAME){if(!this.iframe)throw new Error("Iframe not created during prerender");i?this.submitBodyForm(this.iframe.name,t,s):this.iframe.src=t,this.hostWindow=this.iframe.contentWindow}else{const r=this.buildWindowName();this.hostWindow=mt({url:i?"about:blank":t,name:r,dimensions:this.resolveDimensions()}),i&&this.submitBodyForm(r,t,s);const o=_t(this.hostWindow,()=>{this.destroy()});this.cleanup.register(o)}this.hostWindow&&Ye(this.uid,this.hostWindow)}buildUrl(e=this.resolveUrl()){const s=st(this.props,this.options.props).toString();if(!s)return e;const i=e.includes("?")?"&":"?";return`${e}${i}${s}`}buildBodyParams(){return it(this.props,this.options.props)}submitBodyForm(e,t,s){const i=this.container?.ownerDocument??document,r=i.body??i.documentElement;if(!r)throw new Error("Document root is unavailable for bodyParam form submission");const o=i.createElement("form");o.method="POST",o.action=t,o.target=e,o.style.display="none";for(const[a,c]of s.entries()){const l=i.createElement("input");l.type="hidden",l.name=a,l.value=c,o.appendChild(l)}r.appendChild(o);try{o.submit()}finally{o.remove()}}buildWindowName(){const e=this.getHostDomain(),t=Ee(this.props,this.options.props,e,!1),s=Pe(t,this.options.props,this.bridge),i=this.buildNestedHostRefs(),r=Ke({uid:this.uid,tag:this.options.tag,context:this.context,consumerDomain:Q(),props:s,exports:this.createConsumerExports(),children:i});return qe(r)}buildNestedHostRefs(){if(!this.options.children)return;const e=this.options.children({props:this.props}),t={};for(const[s,i]of Object.entries(e)){const r=xt(i);if(!r)throw new Error(`Nested component "${s}" is missing component metadata`);if(typeof r.url!="string")throw new Error(`Nested component "${s}" must use a static string URL. Function URLs are not supported in children.`);t[s]={tag:r.tag,url:r.url,props:r.props,dimensions:typeof r.dimensions=="function"?void 0:r.dimensions,defaultContext:r.defaultContext}}return Object.keys(t).length>0?t:void 0}createConsumerExports(){return{init:h.INIT,close:h.CLOSE,resize:h.RESIZE,show:h.SHOW,hide:h.HIDE,onError:h.ERROR,updateProps:h.PROPS,export:h.EXPORT}}getHostDomain(){return this.openedHostDomain?this.openedHostDomain:this.resolveUrlOrigin(this.resolveUrl())??"*"}async waitForHost(){this.initPromise=ue();try{await Ue(this.initPromise.promise,this.options.timeout,`Host component "${this.options.tag}" (uid: ${this._uid}) did not initialize within ${this.options.timeout}ms. Check that the host page loads correctly and calls the initialization code.`)}catch(e){throw this.handleError(e),e}}setupMessageHandlers(){this.messenger.on(h.INIT,()=>(this.initPromise&&this.initPromise.resolve(),{success:!0})),this.messenger.on(h.CLOSE,async()=>(await this.close(),{success:!0})),this.messenger.on(h.RESIZE,async e=>(await this.resize(e),{success:!0})),this.messenger.on(h.FOCUS,async()=>(await this.focus(),{success:!0})),this.messenger.on(h.SHOW,async()=>(await this.show(),{success:!0})),this.messenger.on(h.HIDE,async()=>(await this.hide(),{success:!0})),this.messenger.on(h.ERROR,async e=>{const t=new Error(e.message);return t.stack=e.stack,this.handleError(t),{success:!0}}),this.messenger.on(h.EXPORT,async e=>(this.exports=e,{success:!0})),this.messenger.on(h.CONSUMER_EXPORT,async e=>(this.consumerExports=e,{success:!0})),this.messenger.on(h.GET_SIBLINGS,async e=>{const t=[],s=It(e.tag);if(s)for(const i of s.instances)i.uid!==e.uid&&t.push({uid:i.uid,tag:e.tag,exports:i.exports});return t})}setupCleanup(){this.cleanup.register(()=>{this.messenger.destroy(),this.bridge.destroy(),Qe(this.uid)})}handleError(e){this.event.emit(m.ERROR,e),this.callPropCallback("onError",e)}callPropCallback(e,...t){const s=this.props[e];if(typeof s=="function")try{const i=s(...t);i&&typeof i=="object"&&"catch"in i&&typeof i.catch=="function"&&i.catch(r=>{console.error(`Error in async ${e} callback:`,r)})}catch(i){console.error(`Error in ${e} callback:`,i)}}async destroy(){this.destroyed||(this.destroyed=!0,this.initPromise&&(this.initPromise.reject(new Error(`Component "${this.options.tag}" was destroyed before initialization completed`)),this.initPromise=null),this.iframe&&(ut(this.iframe),this.iframe=null),this.context===p.POPUP&&this.hostWindow&>(this.hostWindow),this.hostWindow=null,this.openedHostDomain=null,this.dynamicUrlTrustedOrigin=null,this.prerenderElement&&(this.prerenderElement.remove(),this.prerenderElement=null),await this.cleanup.cleanup(),this.event.emit(m.DESTROY),this.callPropCallback("onDestroy"),this.event.removeAllListeners())}}class vt{constructor(e,t={},s,i=!1){this.propDefinitions=t,this.allowedConsumerDomains=s,this.deferInit=i,this.uid=e.uid,this.tag=e.tag,this.consumerDomain=e.consumerDomain,this.validateConsumerDomain(),this.event=new le,this.messenger=new de(this.uid,window,Q(),this.consumerDomain),this.setupMessageHandlers(),this.consumerWindow=this.resolveConsumerWindow(),this.bridge=new Z(this.messenger),this.hostProps=this.buildHostProps(e),this.exposeHostProps(),this.deferInit||this.flushInit()}hostProps;event;uid;tag;consumerWindow;consumerDomain;messenger;bridge;propsHandlers=new Set;consumerProps;initError=null;destroyed=!1;initSent=!1;deferredInitFlushScheduled=!1;flushInit(){this.destroyed||this.initSent||(this.initSent=!0,this.sendInit())}scheduleDeferredInitFlush(){this.deferredInitFlushScheduled||this.destroyed||this.initSent||(this.deferredInitFlushScheduled=!0,queueMicrotask(()=>{this.deferredInitFlushScheduled=!1,this.flushInit()}))}exposeHostProps(){const e=window;try{Object.defineProperty(e,"hostProps",{configurable:!0,enumerable:!0,get:()=>(this.deferInit&&!this.initSent&&!this.destroyed&&this.scheduleDeferredInitFlush(),this.hostProps),set:t=>{t&&(this.hostProps=t)}})}catch{e.hostProps=this.hostProps}}validateConsumerDomain(){if(this.allowedConsumerDomains&&!$(this.allowedConsumerDomains,this.consumerDomain))throw new Error(`Consumer domain "${this.consumerDomain}" is not allowed for component "${this.tag}"`)}getProps(){return this.hostProps}resolveConsumerWindow(){if(Be()){const e=je();if(e)return e}if(Ve()){const e=ze();if(e)return e}throw new Error("Could not resolve consumer window")}buildHostProps(e){const t=Oe(e.props,this.propDefinitions,this.messenger,this.bridge,this.consumerWindow,this.consumerDomain);return this.consumerProps=t,{...t,uid:this.uid,tag:this.tag,close:()=>this.close(),focus:()=>this.focus(),resize:s=>this.resize(s),show:()=>this.show(),hide:()=>this.hide(),onProps:s=>this.onProps(s),onError:s=>this.onError(s),getConsumer:()=>this.consumerWindow,getConsumerDomain:()=>this.consumerDomain,export:s=>this.exportData(s),consumer:{props:this.consumerProps,export:s=>this.consumerExport(s)},getPeerInstances:s=>this.getPeerInstances(s),children:this.buildNestedComponents(e.children)}}async sendInit(){try{await this.messenger.send(this.consumerWindow,this.consumerDomain,h.INIT,{uid:this.uid,tag:this.tag})}catch(e){const t=e instanceof Error?e:new Error(String(e));this.initError=t,this.event.emit(m.ERROR,{type:"init_failed",message:`Failed to initialize host component: ${t.message}`,error:t}),console.error("Failed to send init message:",e)}}getInitError(){return this.initError}async close(){await this.messenger.send(this.consumerWindow,this.consumerDomain,h.CLOSE,{})}async focus(){window.focus(),await this.messenger.send(this.consumerWindow,this.consumerDomain,h.FOCUS,{})}async resize(e){await this.messenger.send(this.consumerWindow,this.consumerDomain,h.RESIZE,e)}async show(){await this.messenger.send(this.consumerWindow,this.consumerDomain,h.SHOW,{})}async hide(){await this.messenger.send(this.consumerWindow,this.consumerDomain,h.HIDE,{})}onProps(e){return this.propsHandlers.add(e),{cancel:()=>this.propsHandlers.delete(e)}}async onError(e){await this.messenger.send(this.consumerWindow,this.consumerDomain,h.ERROR,{message:e.message,stack:e.stack})}async exportData(e){await this.messenger.send(this.consumerWindow,this.consumerDomain,h.EXPORT,e)}async consumerExport(e){await this.messenger.send(this.consumerWindow,this.consumerDomain,h.CONSUMER_EXPORT,e)}async getPeerInstances(e){return await this.messenger.send(this.consumerWindow,this.consumerDomain,h.GET_SIBLINGS,{uid:this.uid,tag:this.tag,options:e})??[]}buildNestedComponents(e){if(!e)return;const t={};for(const[s,i]of Object.entries(e))try{t[s]=ie({tag:i.tag,url:i.url,props:i.props,dimensions:i.dimensions,defaultContext:i.defaultContext})}catch(r){console.warn(`Failed to create nested component "${s}":`,r)}return Object.keys(t).length>0?t:void 0}setupMessageHandlers(){this.messenger.on(h.PROPS,e=>{try{const t=Oe(e,this.propDefinitions,this.messenger,this.bridge,this.consumerWindow,this.consumerDomain);Object.assign(this.hostProps,t);for(const s of this.propsHandlers)try{s(t)}catch(i){console.error("Error in props handler:",i)}return this.event.emit(m.PROPS,t),{success:!0}}catch(t){const s=t instanceof Error?t:new Error(String(t));throw console.error("Error deserializing props:",s),this.event.emit(m.ERROR,s),s}})}destroy(){this.destroyed||(this.destroyed=!0,this.deferredInitFlushScheduled=!1,this.messenger.destroy(),this.bridge.destroy(),this.event.removeAllListeners(),this.propsHandlers.clear())}}let _=null;function B(n,e,t={}){if(_){if(e){const i=_.getProps().getConsumerDomain();if(!$(e,i))throw Ct(),new Error(`Consumer domain "${i}" is not allowed for this host component`)}return t.deferInit||_.flushInit(),_}if(!ee())return null;const s=Ze();return s?(_=new vt(s,n,e,t.deferInit??!1),_):(console.error("Failed to parse ForgeFrame payload from window.name"),null)}function Re(){return ee()}function ve(){return ee()}function Ce(){return window.hostProps}function Ct(){_&&(_.destroy(),_=null),delete window.hostProps}const I=new Map,De=Symbol("forgeframe.component.options");function Dt(n){if(!n.tag)throw new Error("Component tag is required");if(!/^[a-z][a-z0-9-]*$/.test(n.tag))throw new Error(`Invalid component tag "${n.tag}". Must start with lowercase letter and contain only lowercase letters, numbers, and hyphens.`);if(!n.url)throw new Error("Component url is required");if(typeof n.url=="string")try{new URL(n.url,window.location.origin)}catch{throw new Error(`Invalid component URL "${n.url}". Must be a valid absolute or relative URL.`)}if(I.has(n.tag))throw new Error(`Component "${n.tag}" is already registered`)}function ie(n){Dt(n);const e=[];let t;if(te(n.tag)){const i=B(n.props,n.allowedConsumerDomains);i&&(t=i.hostProps)}const s=function(i={}){const r=new se(n,i);return e.push(r),r.event.once("destroy",()=>{const o=e.indexOf(r);o!==-1&&e.splice(o,1)}),r};return s.instances=e,s.isHost=()=>te(n.tag),s.isEmbedded=()=>te(n.tag),s.hostProps=t,s[De]=n,s.canRenderTo=async i=>{try{return!!(pe(i)||n.domain)}catch{return!1}},I.set(n.tag,s),s}function It(n){return I.get(n)}function xt(n){return n[De]}async function Ie(n){await n.close()}async function re(n){const e=I.get(n);if(!e)return;const t=[...e.instances];await Promise.all(t.map(s=>s.close()))}async function xe(){const n=Array.from(I.keys());await Promise.all(n.map(e=>re(e)))}function Tt(n,e){const t=Object.keys(n),s=Object.keys(e);if(t.length!==s.length)return!1;for(const i of t)if(!Object.prototype.hasOwnProperty.call(e,i)||!Object.is(n[i],e[i]))return!1;return!0}function Te(n,e){const{React:t}=e,{createElement:s,useRef:i,useEffect:r,useState:o,forwardRef:a}=t,c=a(function(b,P){const{onRendered:R,onError:x,onClose:Ne,context:Nt,className:ke,style:$e,...oe}=b,V=i(null),ae=i(null),ce=i(null),[Le,kt]=o(null);return r(()=>{const T=V.current;if(!T)return;const y=n(oe);return ae.current=y,R&&y.event.once("rendered",R),Ne&&y.event.once("close",Ne),x&&y.event.on("error",x),y.render(T,Nt).catch(F=>{kt(F),x?.(F)}),()=>{y.close().catch(()=>{}),ae.current=null,ce.current=null}},[]),r(()=>{const T=ae.current;if(!T)return;const y=oe,F=ce.current;F&&Tt(F,y)||(ce.current=y,T.updateProps(y).catch($t=>{x?.($t)}))},[oe,x]),r(()=>{P&&typeof P=="object"&&V.current&&(P.current=V.current)},[P]),Le?s("div",{className:ke,style:{color:"red",padding:"16px",...$e}},`Error: ${Le.message}`):s("div",{ref:V,className:ke,style:{display:"inline-block",...$e}})}),l=`ForgeFrame(${n.name||"Component"})`;return c.displayName=l,c}function Ft(n){return function(t){return Te(t,{React:n})}}B(void 0,void 0,{deferInit:!0});const Fe={create:ie,destroy:Ie,destroyByTag:re,destroyAll:xe,isHost:Re,isEmbedded:ve,getHostProps:Ce,initHost:B,PROP_SERIALIZATION:v,CONTEXT:p,EVENT:m,PopupOpenError:ne,VERSION:q,isStandardSchema:w,prop:f};u.AnySchema=z,u.ArraySchema=C,u.BooleanSchema=A,u.CONTEXT=p,u.EVENT=m,u.EnumSchema=W,u.ForgeFrame=Fe,u.FunctionSchema=U,u.LiteralSchema=M,u.NumberSchema=H,u.ObjectSchema=D,u.PROP_SERIALIZATION=v,u.PopupOpenError=ne,u.PropSchema=g,u.StringSchema=L,u.VERSION=q,u.create=ie,u.createReactComponent=Te,u.default=Fe,u.destroy=Ie,u.destroyAll=xe,u.destroyByTag=re,u.getHostProps=Ce,u.initHost=B,u.isEmbedded=ve,u.isHost=Re,u.isStandardSchema=w,u.prop=f,u.withReactComponent=Ft,Object.defineProperties(u,{__esModule:{value:!0},[Symbol.toStringTag]:{value:"Module"}})}));
|
|
5
|
+
`,(s.head??s.documentElement).appendChild(n)}function Ht(s){const{doc:e,dimensions:t,uid:n,tag:i}=s,r=e.createElement("div");return r.id=`forgeframe-container-${n}`,r.setAttribute("data-forgeframe-tag",i),Object.assign(r.style,{display:"inline-block",position:"relative",width:O(t.width),height:O(t.height),overflow:"hidden"}),r}function $t(s){const{doc:e,dimensions:t,cspNonce:n}=s;Ut(e,n);const i=e.createElement("div");Object.assign(i.style,{display:"flex",alignItems:"center",justifyContent:"center",width:O(t.width),height:O(t.height),backgroundColor:"#f5f5f5",position:"absolute",top:"0",left:"0",zIndex:"100"});const r=e.createElement("div");return Object.assign(r.style,{width:"40px",height:"40px",border:"3px solid #e0e0e0",borderTopColor:"#3498db",borderRadius:"50%",animation:"forgeframe-spin 1s linear infinite"}),i.appendChild(r),i}function Lt(s,e=200){return new Promise(t=>{s.style.opacity="0",s.style.transition=`opacity ${e}ms ease-in`,s.offsetHeight,s.style.opacity="1",setTimeout(t,e)})}function kt(s,e=200){return new Promise(t=>{s.style.transition=`opacity ${e}ms ease-out`,s.style.opacity="0",setTimeout(t,e)})}async function At(s,e,t){e&&(await kt(e,150),e.remove()),t.style.display="",t.style.visibility="visible",t.style.opacity="0",await Lt(t,150)}class re{event;state={};exports;consumerExports;_uid;get uid(){return this._uid}options;props;inputProps;context;messenger;bridge;cleanup;hostWindow=null;openedHostDomain=null;dynamicUrlTrustedOrigin=null;iframe=null;container=null;prerenderElement=null;initPromise=null;hostInitialized=!1;rendered=!1;destroyed=!1;pendingPropsUpdate=null;constructor(e,t={}){this._uid=We(),this.options=this.normalizeOptions(e),this.context=this.options.defaultContext,this.inputProps={...t},this.event=new he,this.cleanup=new je;const n=this.inputProps,i=this.createPropContext(n);this.props=Ee(n,this.options.props,i);const r=this.buildTrustedDomains();this.messenger=new pe(this.uid,window,L(),r),this.bridge=new Q(this.messenger),this.setupMessageHandlers(),this.setupCleanup()}buildTrustedDomains(){const e=[],t=this.resolveUrlOrigin(this.resolveUrl());if(t&&(e.push(t),this.dynamicUrlTrustedOrigin=t),this.options.domain&&(typeof this.options.domain=="string"?e.push(this.options.domain):Array.isArray(this.options.domain)?e.push(...this.options.domain):this.options.domain instanceof RegExp&&e.push(this.options.domain)),e.length!==0)return e.length===1?e[0]:e}async render(e,t){if(this.destroyed)throw new Error("Component has been destroyed");if(this.rendered)throw new Error("Component has already been rendered");this.context=t??this.options.defaultContext,this.checkEligibility(),Pe(this.props,this.options.props),this.options.validate?.({props:this.props}),this.container=this.resolveContainer(e),this.event.emit(m.PRERENDER),this.callPropCallback("onPrerender"),await this.prerender(),this.event.emit(m.PRERENDERED),this.callPropCallback("onPrerendered"),this.event.emit(m.RENDER),this.callPropCallback("onRender");try{await this.open(),await this.waitForHost(),this.context===f.IFRAME&&this.iframe&&this.prerenderElement&&(await At(this.container,this.prerenderElement,this.iframe),this.prerenderElement=null)}catch(n){throw await this.destroy().catch(()=>{}),n}this.rendered=!0,this.event.emit(m.RENDERED),this.callPropCallback("onRendered"),this.event.emit(m.DISPLAY),this.callPropCallback("onDisplay")}async renderTo(e,t,n){return this.render(t,n)}async close(){this.destroyed||(this.event.emit(m.CLOSE),await this.destroy())}async focus(){this.context===f.IFRAME&&this.iframe?vt(this.iframe):this.context===f.POPUP&&this.hostWindow&&xt(this.hostWindow),this.event.emit(m.FOCUS),this.callPropCallback("onFocus")}async resize(e){this.context===f.IFRAME&&this.iframe?St(this.iframe,e):this.context===f.POPUP&&this.hostWindow&&Ft(this.hostWindow,e),this.event.emit(m.RESIZE,e),this.callPropCallback("onResize",e)}async show(){this.context===f.IFRAME&&this.iframe&&Ot(this.iframe)}async hide(){this.context===f.IFRAME&&this.iframe&&Oe(this.iframe)}async updateProps(e){return this.queuePropsUpdate(()=>this.applyPropsUpdate(e))}async applyPropsUpdate(e){const{nextInputProps:t,nextProps:n}=this.buildNextProps(e),i=this.resolveUrl(n),r=this.resolveUrlOrigin(i);this.assertStableRenderedOrigin(r),this.inputProps=t,this.props=n,this.rendered||this.syncTrustedDomainForUrl(i),this.hostWindow&&!k(this.hostWindow)&&await this.sendPropsUpdateToHost(n),this.emitPropsUpdated()}buildNextProps(e){const t={...this.inputProps,...e},n={...this.props,...e},i=this.createPropContext(n),r=Ee(n,this.options.props,i);return Pe(r,this.options.props),this.options.validate?.({props:r}),{nextInputProps:t,nextProps:r}}assertStableRenderedOrigin(e){if(this.rendered&&this.openedHostDomain&&e&&e!==this.openedHostDomain)throw new Error(`Cannot change component URL origin after render (from "${this.openedHostDomain}" to "${e}")`)}async sendPropsUpdateToHost(e){if(!this.hostWindow||k(this.hostWindow))return;const t=this.openedHostDomain??this.getHostDomain(),n=be(e,this.options.props,t,ge(this.hostWindow)),i=this.serializePropsForHost(n,{finishBatch:!1});try{await this.messenger.send(this.hostWindow,t,h.PROPS,i),this.bridge.finishBatch()}catch(r){throw this.bridge.finishBatch(!0),r}}emitPropsUpdated(){this.event.emit(m.PROPS,this.props),this.callPropCallback("onProps",this.props)}queuePropsUpdate(e){if(!this.pendingPropsUpdate){const n=e();return this.trackPendingUpdateIfHostConnected(n),n}const t=this.pendingPropsUpdate.then(e,e);return this.trackPendingUpdate(t),t}trackPendingUpdateIfHostConnected(e){this.hostWindow&&!k(this.hostWindow)&&this.trackPendingUpdate(e)}trackPendingUpdate(e){const t=e.then(()=>{},()=>{});this.pendingPropsUpdate=t,t.finally(()=>{this.pendingPropsUpdate===t&&(this.pendingPropsUpdate=null)})}clone(){const e=new re(this.options,this.props);return e.inputProps={...this.inputProps},e}isEligible(){return this.options.eligible?this.options.eligible({props:this.props}).eligible:!0}normalizeOptions(e){return{...e,props:e.props??{},defaultContext:e.defaultContext??f.IFRAME,dimensions:e.dimensions??{width:"100%",height:"100%"},timeout:e.timeout??1e4,children:e.children}}resolveUrl(e=this.props){return typeof this.options.url=="function"?this.options.url(e):this.options.url}resolveDimensions(){return typeof this.options.dimensions=="function"?this.options.dimensions(this.props):this.options.dimensions}resolveUrlOrigin(e){try{return new URL(e,window.location.origin).origin}catch{return null}}isExplicitDomainTrust(e){return this.options.domain?S(this.options.domain,e):!1}syncTrustedDomainForUrl(e){const t=this.resolveUrlOrigin(e);if(!t)return;const n=this.dynamicUrlTrustedOrigin;n&&n!==t&&!this.isExplicitDomainTrust(n)&&this.messenger.removeTrustedDomain(n),this.messenger.addTrustedDomain(t),this.dynamicUrlTrustedOrigin=t}createPropContext(e=this.props){return{props:e,state:this.state,close:()=>this.close(),focus:()=>this.focus(),onError:t=>this.handleError(t),container:this.container,uid:this.uid,tag:this.options.tag}}resolveContainer(e){if(!e)throw new Error("Container is required for rendering");if(typeof e=="string"){const t=document.querySelector(e);if(!t)throw new Error(`Container "${e}" not found`);return t}return e}checkEligibility(){if(!this.options.eligible)return;const e=this.options.eligible({props:this.props});if(!e.eligible)throw new Error(`Component not eligible: ${e.reason??"Unknown reason"}`)}async prerender(){if(!this.container)return;const e=this.options.prerenderTemplate??$t,t=this.options.containerTemplate??Ht,n=this.resolveDimensions(),i=this.props.cspNonce;if(this.context===f.IFRAME){const c=this.buildWindowName();this.iframe=this.createIframeElement(c),Oe(this.iframe)}const r={uid:this.uid,tag:this.options.tag,context:this.context,dimensions:n,props:this.props,doc:document,container:this.container,frame:this.iframe,prerenderFrame:null,close:()=>this.close(),focus:()=>this.focus(),cspNonce:i};this.prerenderElement=e(r);const o={uid:this.uid,tag:this.options.tag,context:this.context,dimensions:n,props:this.props,doc:document,container:this.container,frame:this.iframe,prerenderFrame:this.prerenderElement,close:()=>this.close(),focus:()=>this.focus(),cspNonce:i},a=t(o);a&&(this.container.appendChild(a),this.container=a),this.prerenderElement&&!this.prerenderElement.parentNode&&this.container.appendChild(this.prerenderElement),this.iframe&&!this.iframe.parentNode&&this.container.appendChild(this.iframe)}createIframeElement(e){const t=document.createElement("iframe"),n=this.resolveDimensions(),i=typeof this.options.attributes=="function"?this.options.attributes(this.props):this.options.attributes??{},r=typeof this.options.style=="function"?this.options.style(this.props):this.options.style??{};t.name=e,t.setAttribute("frameborder","0"),t.setAttribute("allowtransparency","true"),t.setAttribute("scrolling","auto"),n.width!==void 0&&(t.style.width=typeof n.width=="number"?`${n.width}px`:n.width),n.height!==void 0&&(t.style.height=typeof n.height=="number"?`${n.height}px`:n.height);for(const[o,a]of Object.entries(i))a!==void 0&&(typeof a=="boolean"?a&&t.setAttribute(o,""):t.setAttribute(o,a));for(const[o,a]of Object.entries(r)){if(a===void 0)continue;const c=typeof a=="number"?`${a}px`:a;t.style.setProperty(o.replace(/([A-Z])/g,"-$1").toLowerCase(),String(c))}return i.sandbox||t.setAttribute("sandbox","allow-scripts allow-same-origin allow-forms allow-popups allow-popups-to-escape-sandbox"),t}async open(){const e=this.resolveUrl();this.syncTrustedDomainForUrl(e),this.openedHostDomain=this.resolveUrlOrigin(e);const t=this.buildUrl(e),n=this.buildBodyParams(),i=n.toString().length>0;if(this.context===f.IFRAME){if(!this.iframe)throw new Error("Iframe not created during prerender");i?this.submitBodyForm(this.iframe.name,t,n):this.iframe.src=t,this.hostWindow=this.iframe.contentWindow}else{const r=this.buildWindowName();this.hostWindow=Ct({url:i?"about:blank":t,name:r,dimensions:this.resolveDimensions()}),i&&this.submitBodyForm(r,t,n);const o=Nt(this.hostWindow,()=>{this.close()});this.cleanup.register(o)}this.hostWindow&&ut(this.uid,this.hostWindow)}buildUrl(e=this.resolveUrl()){const n=mt(this.props,this.options.props).toString();if(!n)return e;const i=e.includes("?")?"&":"?";return`${e}${i}${n}`}buildBodyParams(){return gt(this.props,this.options.props)}submitBodyForm(e,t,n){const i=this.container?.ownerDocument??document,r=i.body??i.documentElement;if(!r)throw new Error("Document root is unavailable for bodyParam form submission");const o=i.createElement("form");o.method="POST",o.action=t,o.target=e,o.style.display="none";for(const[a,c]of n.entries()){const l=i.createElement("input");l.type="hidden",l.name=a,l.value=c,o.appendChild(l)}r.appendChild(o);try{o.submit()}finally{o.remove()}}buildWindowName(){const e=this.getHostDomain(),t=be(this.props,this.options.props,e,!1),n=this.serializePropsForHost(t),i=this.buildNestedHostRefs(),r=at({uid:this.uid,tag:this.options.tag,context:this.context,consumerDomain:L(),props:n,exports:this.createConsumerExports(),children:i});return it(r)}serializePropsForHost(e,t){this.bridge.startBatch();const n=t?.finishBatch??!0;try{const i=_t(e,this.options.props,this.bridge);return n&&this.bridge.finishBatch(),i}catch(i){throw this.bridge.finishBatch(!0),i}}buildNestedHostRefs(){if(!this.options.children)return;const e=this.options.children({props:this.props}),t={};for(const[n,i]of Object.entries(e)){const r=qt(i);if(!r)throw new Error(`Nested component "${n}" is missing component metadata`);if(typeof r.url!="string")throw new Error(`Nested component "${n}" must use a static string URL. Function URLs are not supported in children.`);t[n]={tag:r.tag,url:r.url,props:r.props,dimensions:typeof r.dimensions=="function"?void 0:r.dimensions,defaultContext:r.defaultContext}}return Object.keys(t).length>0?t:void 0}createConsumerExports(){return{init:h.INIT,close:h.CLOSE,resize:h.RESIZE,show:h.SHOW,hide:h.HIDE,onError:h.ERROR,updateProps:h.PROPS,export:h.EXPORT}}getHostDomain(){return this.openedHostDomain?this.openedHostDomain:this.resolveUrlOrigin(this.resolveUrl())??"*"}async waitForHost(){if(this.hostInitialized)return;const e=de();this.initPromise=e;try{await Be(e.promise,this.options.timeout,`Host component "${this.options.tag}" (uid: ${this._uid}) did not initialize within ${this.options.timeout}ms. Check that the host page loads correctly and calls the initialization code.`)}catch(t){throw this.handleError(t),t}finally{this.initPromise===e&&(this.initPromise=null)}}setupMessageHandlers(){this.setupHostLifecycleHandlers(),this.setupHostDataHandlers()}setupHostLifecycleHandlers(){this.messenger.on(h.INIT,()=>(this.hostInitialized=!0,this.initPromise&&this.initPromise.resolve(),{success:!0})),this.messenger.on(h.CLOSE,async()=>(await this.close(),{success:!0})),this.messenger.on(h.RESIZE,async e=>(await this.resize(e),{success:!0})),this.messenger.on(h.FOCUS,async()=>(await this.focus(),{success:!0})),this.messenger.on(h.SHOW,async()=>(await this.show(),{success:!0})),this.messenger.on(h.HIDE,async()=>(await this.hide(),{success:!0})),this.messenger.on(h.ERROR,async e=>{const t=new Error(e.message);return t.stack=e.stack,this.handleError(t),{success:!0}})}setupHostDataHandlers(){this.messenger.on(h.EXPORT,async e=>(this.exports=e,{success:!0})),this.messenger.on(h.CONSUMER_EXPORT,async e=>(this.consumerExports=e,{success:!0})),this.messenger.on(h.GET_SIBLINGS,async e=>this.getSiblingInstances(e))}getSiblingInstances(e){const t=[];if(e.options?.anyConsumer){for(const[i,r]of Vt())for(const o of r.instances)o.uid!==e.uid&&t.push({uid:o.uid,tag:i,exports:o.exports});return t}const n=Bt(e.tag);if(!n)return t;for(const i of n.instances)i.uid!==e.uid&&t.push({uid:i.uid,tag:e.tag,exports:i.exports});return t}setupCleanup(){this.cleanup.register(()=>{this.messenger.destroy(),this.bridge.destroy(),ht(this.uid)})}handleError(e){this.event.emit(m.ERROR,e),this.callPropCallback("onError",e)}callPropCallback(e,...t){const n=this.props[e];if(typeof n=="function")try{const i=n(...t);i&&typeof i=="object"&&"catch"in i&&typeof i.catch=="function"&&i.catch(r=>{console.error(`Error in async ${e} callback:`,r)})}catch(i){console.error(`Error in ${e} callback:`,i)}}async destroy(){this.destroyed||(this.destroyed=!0,this.initPromise&&(this.initPromise.reject(new Error(`Component "${this.options.tag}" was destroyed before initialization completed`)),this.initPromise=null),this.hostInitialized=!1,this.iframe&&(Rt(this.iframe),this.iframe=null),this.context===f.POPUP&&this.hostWindow&&Dt(this.hostWindow),this.hostWindow=null,this.openedHostDomain=null,this.dynamicUrlTrustedOrigin=null,this.prerenderElement&&(this.prerenderElement.remove(),this.prerenderElement=null),await this.cleanup.cleanup(),this.event.emit(m.DESTROY),this.callPropCallback("onDestroy"),this.event.removeAllListeners())}}const Ie="Could not resolve consumer window",Mt=new Set(["uid","tag","close","focus","resize","show","hide","onProps","onError","getConsumer","getConsumerDomain","export","consumer","getPeerInstances","children"]);class zt{constructor(e,t={},n,i=!1){this.propDefinitions=t,this.allowedConsumerDomains=n,this.deferInit=i,this.uid=e.uid,this.tag=e.tag,this.consumerDomain=e.consumerDomain,this.validateConsumerDomain(),this.event=new he,this.messenger=new pe(this.uid,window,L(),this.consumerDomain);let r=null;try{this.setupMessageHandlers(),this.consumerWindow=this.resolveConsumerWindow(),r=new Q(this.messenger),this.bridge=r,this.hostProps=this.buildHostProps(e),this.exposeHostProps(),this.deferInit||this.flushInit()}catch(o){throw r?.destroy(),this.messenger.destroy(),this.event.removeAllListeners(),this.propsHandlers.clear(),o}}hostProps;event;uid;tag;consumerWindow;consumerDomain;messenger;bridge;propsHandlers=new Set;consumerProps;initError=null;destroyed=!1;initSent=!1;deferredInitFlushScheduled=!1;flushInit(){this.destroyed||this.initSent||(this.initSent=!0,this.sendInit())}scheduleDeferredInitFlush(){this.deferredInitFlushScheduled||this.destroyed||this.initSent||(this.deferredInitFlushScheduled=!0,queueMicrotask(()=>{this.deferredInitFlushScheduled=!1,this.flushInit()}))}exposeHostProps(){const e=window;try{Object.defineProperty(e,"hostProps",{configurable:!0,enumerable:!0,get:()=>(this.deferInit&&!this.initSent&&!this.destroyed&&this.scheduleDeferredInitFlush(),this.hostProps),set:t=>{t&&(this.hostProps=t)}})}catch{e.hostProps=this.hostProps}}validateConsumerDomain(){if(this.allowedConsumerDomains&&!S(this.allowedConsumerDomains,this.consumerDomain))throw new Error(`Consumer domain "${this.consumerDomain}" is not allowed for component "${this.tag}"`)}getProps(){return this.hostProps}resolveConsumerWindow(){if(st()){const e=tt();if(e)return e}if(nt()){const e=et();if(e)return e}throw new Error(Ie)}buildHostProps(e){const t=Se(e.props,this.propDefinitions,this.messenger,this.bridge,this.consumerWindow,this.consumerDomain);return this.consumerProps=t,{...t,uid:this.uid,tag:this.tag,close:()=>this.close(),focus:()=>this.focus(),resize:n=>this.resize(n),show:()=>this.show(),hide:()=>this.hide(),onProps:n=>this.onProps(n),onError:n=>this.onError(n),getConsumer:()=>this.consumerWindow,getConsumerDomain:()=>this.consumerDomain,export:n=>this.exportData(n),consumer:{props:this.consumerProps,export:n=>this.consumerExport(n)},getPeerInstances:n=>this.getPeerInstances(n),children:this.buildNestedComponents(e.children)}}async sendInit(){try{await this.messenger.send(this.consumerWindow,this.consumerDomain,h.INIT,{uid:this.uid,tag:this.tag})}catch(e){const t=e instanceof Error?e:new Error(String(e));this.initError=t,this.event.emit(m.ERROR,{type:"init_failed",message:`Failed to initialize host component: ${t.message}`,error:t}),console.error("Failed to send init message:",e)}}getInitError(){return this.initError}async close(){await this.messenger.send(this.consumerWindow,this.consumerDomain,h.CLOSE,{})}async focus(){window.focus(),await this.messenger.send(this.consumerWindow,this.consumerDomain,h.FOCUS,{})}async resize(e){await this.messenger.send(this.consumerWindow,this.consumerDomain,h.RESIZE,e)}async show(){await this.messenger.send(this.consumerWindow,this.consumerDomain,h.SHOW,{})}async hide(){await this.messenger.send(this.consumerWindow,this.consumerDomain,h.HIDE,{})}onProps(e){return this.propsHandlers.add(e),{cancel:()=>this.propsHandlers.delete(e)}}async onError(e){await this.messenger.send(this.consumerWindow,this.consumerDomain,h.ERROR,{message:e.message,stack:e.stack})}async exportData(e){await this.messenger.send(this.consumerWindow,this.consumerDomain,h.EXPORT,e)}async consumerExport(e){await this.messenger.send(this.consumerWindow,this.consumerDomain,h.CONSUMER_EXPORT,e)}async getPeerInstances(e){return await this.messenger.send(this.consumerWindow,this.consumerDomain,h.GET_SIBLINGS,{uid:this.uid,tag:this.tag,options:e})??[]}buildNestedComponents(e){if(!e)return;const t={};for(const[n,i]of Object.entries(e))try{t[n]=oe({tag:i.tag,url:i.url,props:i.props,dimensions:i.dimensions,defaultContext:i.defaultContext})}catch(r){console.warn(`Failed to create nested component "${n}":`,r)}return Object.keys(t).length>0?t:void 0}setupMessageHandlers(){this.messenger.on(h.PROPS,e=>{try{const t=this.consumerProps,n=Se(e,this.propDefinitions,this.messenger,this.bridge,this.consumerWindow,this.consumerDomain);this.removeStaleHostProps(t,n),this.consumerProps=n,Object.assign(this.hostProps,n),this.hostProps.consumer.props=this.consumerProps;for(const i of this.propsHandlers)try{i(n)}catch(r){console.error("Error in props handler:",r)}return this.event.emit(m.PROPS,n),{success:!0}}catch(t){const n=t instanceof Error?t:new Error(String(t));throw console.error("Error deserializing props:",n),this.event.emit(m.ERROR,n),n}})}removeStaleHostProps(e,t){for(const n of Object.keys(e))n in t||Mt.has(n)||delete this.hostProps[n]}destroy(){this.destroyed||(this.destroyed=!0,this.deferredInitFlushScheduled=!1,this.messenger.destroy(),this.bridge.destroy(),this.event.removeAllListeners(),this.propsHandlers.clear())}}let w=null;function J(s,e,t={}){if(w){if(e){const i=w.getProps().getConsumerDomain();if(!S(e,i))throw Wt(),new Error(`Consumer domain "${i}" is not allowed for this host component`)}return t.deferInit||w.flushInit(),w}if(!se())return null;const n=ct();if(!n)return console.error("Failed to parse ForgeFrame payload from window.name"),null;try{w=new zt(n,s,e,t.deferInit??!1)}catch(i){if(i instanceof Error&&i.message===Ie)return null;throw i}return w}function Ce(){return se()}function De(){return se()}function xe(){return window.hostProps}function Wt(){w&&(w.destroy(),w=null),delete window.hostProps}const v=new Map,Te=Symbol("forgeframe.component.options");function jt(s){if(!s.tag)throw new Error("Component tag is required");if(!/^[a-z][a-z0-9-]*$/.test(s.tag))throw new Error(`Invalid component tag "${s.tag}". Must start with lowercase letter and contain only lowercase letters, numbers, and hyphens.`);if(!s.url)throw new Error("Component url is required");if(typeof s.url=="string")try{new URL(s.url,window.location.origin)}catch{throw new Error(`Invalid component URL "${s.url}". Must be a valid absolute or relative URL.`)}if(v.has(s.tag))throw new Error(`Component "${s.tag}" is already registered`)}function oe(s){jt(s);const e=[];let t;if(ne(s.tag)){const i=J(s.props,s.allowedConsumerDomains);i&&(t=i.hostProps)}const n=function(i={}){const r=new re(s,i);return e.push(r),r.event.once("destroy",()=>{const o=e.indexOf(r);o!==-1&&e.splice(o,1)}),r};return n.instances=e,n.isHost=()=>ne(s.tag),n.isEmbedded=()=>ne(s.tag),n.hostProps=t,n[Te]=s,n.canRenderTo=async i=>{try{if(ge(i))return!0;const r=L(i);return s.domain?r?S(s.domain,r):!0:!1}catch{return!1}},v.set(s.tag,n),n}function Bt(s){return v.get(s)}function Vt(){return Array.from(v.entries())}function qt(s){return s[Te]}async function Ne(s){await s.close()}async function ae(s){const e=v.get(s);if(!e)return;const t=[...e.instances];await Promise.all(t.map(n=>n.close()))}async function Fe(){const s=Array.from(v.keys());await Promise.all(s.map(e=>ae(e)))}function Jt(s,e){const t=Object.keys(s),n=Object.keys(e);if(t.length!==n.length)return!1;for(const i of t)if(!Object.prototype.hasOwnProperty.call(e,i)||!Object.is(s[i],e[i]))return!1;return!0}function Ue(s,e){const{React:t}=e,{createElement:n,useRef:i,useEffect:r,useState:o,forwardRef:a}=t,c=a(function(b,E){const{onRendered:$e,onError:ce,onClose:Le,context:Kt,className:ke,style:Ae,...Me}=b,X=i(null),le=i(null),ue=i(null),K=i(ce),[ze,Yt]=o(null);return r(()=>{K.current=ce},[ce]),r(()=>{const F=X.current;if(!F)return;const y=s(Me);return le.current=y,$e&&y.event.once("rendered",$e),Le&&y.event.once("close",Le),y.event.on("error",R=>{K.current?.(R)}),y.render(F,Kt).catch(R=>{Yt(R),K.current?.(R)}),()=>{y.close().catch(()=>{}),le.current=null,ue.current=null}},[]),r(()=>{const F=le.current;if(!F)return;const y=Me,R=ue.current;R&&Jt(R,y)||(ue.current=y,F.updateProps(y).catch(Zt=>{K.current?.(Zt)}))}),r(()=>{E&&typeof E=="object"&&X.current&&(E.current=X.current)},[E]),ze?n("div",{className:ke,style:{color:"red",padding:"16px",...Ae}},`Error: ${ze.message}`):n("div",{ref:X,className:ke,style:{display:"inline-block",...Ae}})}),l=`ForgeFrame(${s.name||"Component"})`;return c.displayName=l,c}function Xt(s){return function(t){return Ue(t,{React:s})}}J(void 0,void 0,{deferInit:!0});const He={create:oe,destroy:Ne,destroyByTag:ae,destroyAll:Fe,isHost:Ce,isEmbedded:De,getHostProps:xe,initHost:J,PROP_SERIALIZATION:I,CONTEXT:f,EVENT:m,PopupOpenError:ie,VERSION:Y,isStandardSchema:T,prop:d};u.AnySchema=V,u.ArraySchema=D,u.BooleanSchema=z,u.CONTEXT=f,u.EVENT=m,u.EnumSchema=B,u.ForgeFrame=He,u.FunctionSchema=W,u.LiteralSchema=j,u.NumberSchema=M,u.ObjectSchema=x,u.PROP_SERIALIZATION=I,u.PopupOpenError=ie,u.PropSchema=g,u.StringSchema=A,u.VERSION=Y,u.create=oe,u.createReactComponent=Ue,u.default=He,u.destroy=Ne,u.destroyAll=Fe,u.destroyByTag=ae,u.getHostProps=xe,u.initHost=J,u.isEmbedded=De,u.isHost=Ce,u.isStandardSchema=T,u.prop=d,u.withReactComponent=Xt,Object.defineProperties(u,{__esModule:{value:!0},[Symbol.toStringTag]:{value:"Module"}})}));
|
package/dist/props/prop.d.ts
CHANGED
|
@@ -450,6 +450,8 @@ export declare class LiteralSchema<T extends string | number | boolean> extends
|
|
|
450
450
|
export declare class EnumSchema<T extends string | number> extends PropSchema<T> {
|
|
451
451
|
/** @internal */
|
|
452
452
|
private _values;
|
|
453
|
+
/** @internal */
|
|
454
|
+
private _valueSet;
|
|
453
455
|
constructor(values: readonly T[]);
|
|
454
456
|
/** @internal */
|
|
455
457
|
protected _validate(value: unknown): StandardSchemaV1Result<T>;
|
package/dist/types.d.ts
CHANGED
|
@@ -13,7 +13,8 @@ import type { StandardSchemaV1, InferOutput } from './props/schema';
|
|
|
13
13
|
* Pattern for matching domains in security configurations.
|
|
14
14
|
*
|
|
15
15
|
* @remarks
|
|
16
|
-
* Can be a single domain string, a RegExp pattern, or an array of
|
|
16
|
+
* Can be a single domain string, a RegExp pattern, or an array of string/RegExp patterns.
|
|
17
|
+
* String patterns support `*` wildcards (for example, `'https://*.example.com'`).
|
|
17
18
|
*
|
|
18
19
|
* @example
|
|
19
20
|
* ```typescript
|
|
@@ -23,13 +24,14 @@ import type { StandardSchemaV1, InferOutput } from './props/schema';
|
|
|
23
24
|
* // RegExp pattern
|
|
24
25
|
* const pattern: DomainMatcher = /^https:\/\/.*\.example\.com$/;
|
|
25
26
|
*
|
|
26
|
-
* // Multiple
|
|
27
|
-
* const domains: DomainMatcher = ['https
|
|
27
|
+
* // Multiple patterns (can mix strings and RegExp)
|
|
28
|
+
* const domains: DomainMatcher = ['https://*.example.com', /^https:\/\/api\d+\.example\.com$/];
|
|
28
29
|
* ```
|
|
29
30
|
*
|
|
30
31
|
* @public
|
|
31
32
|
*/
|
|
32
|
-
|
|
33
|
+
type DomainPattern = string | RegExp;
|
|
34
|
+
export type DomainMatcher = DomainPattern | DomainPattern[];
|
|
33
35
|
/**
|
|
34
36
|
* Component dimension specification.
|
|
35
37
|
*
|
|
@@ -606,6 +608,9 @@ export interface ForgeFrameComponentInstance<P = Record<string, unknown>, X = un
|
|
|
606
608
|
/**
|
|
607
609
|
* Update the component's props.
|
|
608
610
|
*
|
|
611
|
+
* @remarks
|
|
612
|
+
* Props are normalized and validated before being sent to the host.
|
|
613
|
+
*
|
|
609
614
|
* @param props - Partial props to merge with existing
|
|
610
615
|
* @returns Promise that resolves when props are updated
|
|
611
616
|
*/
|
|
@@ -750,7 +755,8 @@ export interface SiblingInfo {
|
|
|
750
755
|
*/
|
|
751
756
|
export interface GetPeerInstancesOptions {
|
|
752
757
|
/**
|
|
753
|
-
* If true,
|
|
758
|
+
* If true, include peers from all registered component tags in the current
|
|
759
|
+
* consumer context.
|
|
754
760
|
* @defaultValue false
|
|
755
761
|
*/
|
|
756
762
|
anyConsumer?: boolean;
|
package/dist/utils/cleanup.d.ts
CHANGED
|
@@ -43,9 +43,10 @@ export declare class CleanupManager {
|
|
|
43
43
|
* @param task - The cleanup function to register
|
|
44
44
|
*
|
|
45
45
|
* @remarks
|
|
46
|
-
* If cleanup has already been performed, the task is
|
|
47
|
-
* rather than being registered. This ensures
|
|
48
|
-
* still handled appropriately
|
|
46
|
+
* If cleanup has already been performed, the task is scheduled on a microtask
|
|
47
|
+
* and executed asynchronously rather than being registered. This ensures
|
|
48
|
+
* late-registered tasks are still handled appropriately while safely capturing
|
|
49
|
+
* both sync and async task failures.
|
|
49
50
|
*
|
|
50
51
|
* @example
|
|
51
52
|
* ```typescript
|
package/dist/window/helpers.d.ts
CHANGED
|
@@ -54,7 +54,8 @@ export declare function isSameDomain(win: Window, reference?: Window): boolean;
|
|
|
54
54
|
* @remarks
|
|
55
55
|
* Pattern matching rules:
|
|
56
56
|
* - `"*"` matches any domain
|
|
57
|
-
* - String patterns require exact match
|
|
57
|
+
* - String patterns without wildcards require exact match
|
|
58
|
+
* - String patterns with `*` use wildcard matching (for example, `'https://*.example.com'`)
|
|
58
59
|
* - RegExp patterns use `.test()` method
|
|
59
60
|
* - Array patterns return `true` if any element matches (OR logic)
|
|
60
61
|
*
|
|
@@ -66,6 +67,9 @@ export declare function isSameDomain(win: Window, reference?: Window): boolean;
|
|
|
66
67
|
* // Exact match
|
|
67
68
|
* matchDomain('https://example.com', 'https://example.com'); // true
|
|
68
69
|
*
|
|
70
|
+
* // Wildcard string match
|
|
71
|
+
* matchDomain('https://*.example.com', 'https://sub.example.com'); // true
|
|
72
|
+
*
|
|
69
73
|
* // RegExp match
|
|
70
74
|
* matchDomain(/\.example\.com$/, 'https://sub.example.com'); // true
|
|
71
75
|
*
|