forgeframe 0.0.2 → 0.0.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +44 -4
- package/dist/constants.d.ts +1 -5
- package/dist/core/consumer.d.ts +10 -0
- package/dist/forgeframe.js +271 -215
- package/dist/forgeframe.umd.cjs +2 -2
- package/dist/index.d.ts +1 -1
- package/dist/props/index.d.ts +1 -1
- package/dist/props/normalize.d.ts +11 -0
- package/dist/props/schema.d.ts +11 -3
- package/package.json +7 -2
package/dist/forgeframe.umd.cjs
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
(function(h,p){typeof exports=="object"&&typeof module<"u"?p(exports):typeof define=="function"&&define.amd?define(["exports"],p):(h=typeof globalThis<"u"?globalThis:h||self,p(h.ForgeFrame={}))})(this,(function(h){"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"},F={REQUEST:"request",RESPONSE:"response"},u={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"},$="__forgeframe__",q="0.0.1";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 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 He(){const s=Date.now().toString(36),e=Math.random().toString(36).slice(2,11);return`${s}_${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(n){console.error("Error in cleanup task:",n)}}isCleaned(){return this.cleaned}reset(){this.tasks=[],this.cleaned=!1}}function he(){let s,e;return{promise:new Promise((n,i)=>{s=n,e=i}),resolve:s,reject:e}}function Ue(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 X="forgeframe:";function K(s){return X+JSON.stringify(s)}function Me(s){if(typeof s!="string"||!s.startsWith(X))return null;try{const e=s.slice(X.length),t=JSON.parse(e);return!t.id||!t.type||!t.name||!t.source?null:t}catch{return null}}function ue(s,e,t,n){return{id:s,type:F.REQUEST,name:e,data:t,source:n}}function We(s,e,t,n){return{id:s,type:F.RESPONSE,name:"response",data:e,source:t,error:n?{message:n.message,stack:n.stack}:void 0}}class de{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=[];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,n,i,r=1e4){if(this.destroyed)throw new Error("Messenger has been destroyed");const o=J(),a=ue(o,n,i,{uid:this.uid,domain:this.domain}),c=he(),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(K(a),t)}catch(d){throw this.pending.delete(o),clearTimeout(l),d}return c.promise}post(e,t,n,i){if(this.destroyed)throw new Error("Messenger has been destroyed");const r=J(),o=ue(r,n,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,n){if(e.type===F.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===F.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),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 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 n=J();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>=fe){const a=this.remoteFunctions.keys().next().value;a&&this.remoteFunctions.delete(a)}const o=async(...a)=>this.messenger.send(t,n,u.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(u.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 Y(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");return t.add(s),s.map(n=>Y(n,e,t))}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);const n={};for(const[i,r]of Object.entries(s))n[i]=Y(r,e,t);return n}return s}function G(s,e,t,n,i=new WeakSet){if(Z.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");return i.add(s),s.map(r=>G(r,e,t,n,i))}if(typeof s=="object"&&s!==null){if(i.has(s))throw new Error("Circular reference detected in serialized props");i.add(s);const r={};for(const[o,a]of Object.entries(s))r[o]=G(a,e,t,n,i);return r}return s}function Q(s=window){try{return s.location.origin}catch{return""}}function pe(s,e=window){try{return s.location.origin===e.location.origin}catch{return!1}}function k(s,e){return typeof s=="string"?s==="*"?!0:s===e:s instanceof RegExp?s.test(e):Array.isArray(s)?s.some(t=>k(t,e)):!1}function me(s){if(!s)return!0;try{return s.closed}catch{return!0}}function ze(s=window){try{return s.opener}catch{return null}}function je(s=window){try{const e=s.parent;return e&&e!==s?e:null}catch{return null}}function Be(s=window){try{return s.parent!==s}catch{return!0}}function Ve(s=window){try{return s.opener!==null&&s.opener!==void 0}catch{return!1}}const ge=32*1024;function qe(s){const e=Je(s);return`${$}${e}`}function ye(s){if(!s||!s.startsWith($))return null;const e=s.slice($.length);return Xe(e)}function ee(s=window){try{return s.name.startsWith($)}catch{return!1}}function te(s,e=window){return ye(e.name)?.tag===s}function Je(s){try{const e=JSON.stringify(s),t=btoa(encodeURIComponent(e)),n=new Blob([t]).size;if(n>ge)throw new Error(`Payload size (${Math.round(n/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(s){try{const e=decodeURIComponent(atob(s));return JSON.parse(e)}catch{return null}}function Ke(s){return{uid:s.uid,tag:s.tag,version:q,context:s.context,consumerDomain:s.consumerDomain,props:s.props,exports:s.exports,children:s.children}}function Ze(s=window){return ye(s.name)}const we=100,_=new Map;function Ye(){const s=[];for(const[e,t]of _.entries())me(t)&&s.push(e);for(const e of s)_.delete(e)}function Ge(s,e){if(_.size>=we&&Ye(),_.size>=we){const t=_.keys().next().value;t&&_.delete(t)}_.set(s,e)}function Qe(s){_.delete(s)}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 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 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 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 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 I 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 I;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 I;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 I,literal:s=>new M(s),enum:s=>new W(s),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 E(s){return typeof s=="object"&&s!==null&&"~standard"in s&&typeof s["~standard"]=="object"&&s["~standard"]!==null&&s["~standard"].version===1&&typeof s["~standard"].validate=="function"}function et(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=>`${tt(r.path,t)}: ${r.message}`);throw new Error(`Validation failed: ${i.join("; ")}`)}return n.value}function tt(s,e){if(!s||s.length===0)return e;const t=s.map(n=>typeof n=="object"&&n!==null&&"key"in n?String(n.key):String(n));return`${e}.${t.join(".")}`}function _e(s,e,t){const n={...O,...e},i={};for(const[r,o]of Object.entries(n)){const c=E(o)?{schema:o}:o;let l;const d=c.alias,b=r in s,P=d&&d in s;if(b)l=s[r];else if(P)l=s[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&&E(c.schema)){const S=c.schema["~standard"].validate(void 0);!(S instanceof Promise)&&!S.issues&&(l=S.value)}l!==void 0&&c.decorate&&(l=c.decorate({value:l,props:i})),i[r]=l}return i}function st(s,e){const t={...O,...e};for(const[n,i]of Object.entries(t)){const r=E(i),o=r?{schema:i}: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&&E(o.schema))(a!==void 0||r)&&(a=et(o.schema,a,n),s[n]=a);else if(a===void 0)continue;o.validate&&o.validate({value:a,props:s})}}function Ee(s,e,t,n){const i={...O,...e},r={};for(const[o,a]of Object.entries(i)){const l=E(a)?{schema:a}:a,d=s[o];if(l.sendToHost===!1||l.sameDomain&&!n)continue;if(l.trustedDomains){const P=l.trustedDomains;if(!k(P,t))continue}let b=d;l.hostDecorate&&d!==void 0&&(b=l.hostDecorate({value:d,props:s})),r[o]=b}return r}function nt(s,e){const t=new URLSearchParams,n={...O,...e};for(const[i,r]of Object.entries(n)){const a=E(r)?{schema:r}:r,c=s[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 be(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(be(i,r));else{const o=encodeURIComponent(JSON.stringify(i));t.push(`${r}=${o}`)}}return t.filter(Boolean).join("&")}function it(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 d=a[l];(!(d in c)||typeof c[d]!="object")&&(c[d]={}),c=c[d]}c[a[a.length-1]]=o}return e}function rt(s){return typeof s=="object"&&s!==null&&s.__type__==="dotify"&&typeof s.__value__=="string"}function Pe(s,e,t){const n={...O,...e},i={};for(const[r,o]of Object.entries(s)){if(o===void 0)continue;const a=n[r];i[r]=ot(o,a,t)}return i}function ot(s,e,t){if(typeof s=="function")return t.serialize(s);const n=e?.serialization??v.JSON;if(n===v.BASE64&&typeof s=="object"){const i=JSON.stringify(s);return{__type__:"base64",__value__:btoa(encodeURIComponent(i))}}return n===v.DOTIFY&&typeof s=="object"&&s!==null&&!Array.isArray(s)?{__type__:"dotify",__value__:be(s)}:Y(s,t)}function Oe(s,e,t,n,i,r){const o={...O,...e},a={};for(const[c,l]of Object.entries(s)){const d=o[c];a[c]=at(l,d,t,n,i,r)}return a}function at(s,e,t,n,i,r){if(ct(s))try{const o=decodeURIComponent(atob(s.__value__));return JSON.parse(o)}catch{return s}if(rt(s))try{return it(s.__value__)}catch{return s}return G(s,n,i,r)}function ct(s){return typeof s=="object"&&s!==null&&s.__type__==="base64"&&typeof s.__value__=="string"}function R(s,e="100%"){return s===void 0?e:typeof s=="number"?`${s}px`:s}function j(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 lt(s){try{s.src="about:blank",s.parentNode?.removeChild(s)}catch{}}function ht(s,e){ft(s,e)}function ut(s){s.style.display="",s.style.visibility="visible"}function Re(s){s.style.display="none",s.style.visibility="hidden"}function dt(s){try{s.focus(),s.contentWindow?.focus()}catch{}}function ft(s,e){e.width!==void 0&&(s.style.width=R(e.width)),e.height!==void 0&&(s.style.height=R(e.height))}class se extends Error{constructor(e="Popup blocked by browser"){super(e),this.name="PopupOpenError"}}function pt(s){const{url:e,name:t,dimensions:n}=s,i=j(n.width,500),r=j(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||yt(l))throw new se;return l}function mt(s){try{s.closed||s.close()}catch{}}function gt(s){try{s.closed||s.focus()}catch{}}function yt(s){if(!s)return!0;try{return!!(s.closed||s.innerHeight===0||s.innerWidth===0)}catch{return!0}}function wt(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)}},d=()=>{if(!c){try{if(s.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 _t(s,e){try{const t=j(e.width,s.outerWidth),n=j(e.height,s.outerHeight);s.resizeTo(t,n)}catch{}}function Et(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:R(t.width),height:R(t.height),overflow:"hidden"}),r}function bt(s){const{doc:e,dimensions:t,cspNonce:n}=s,i=e.createElement("div");Object.assign(i.style,{display:"flex",alignItems:"center",justifyContent:"center",width:R(t.width),height:R(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 n&&o.setAttribute("nonce",n),o.textContent=`
|
|
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=`
|
|
2
2
|
@keyframes forgeframe-spin {
|
|
3
3
|
to { transform: rotate(360deg); }
|
|
4
4
|
}
|
|
5
|
-
`,i.appendChild(o),i.appendChild(r),i}function Pt(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 Ot(s,e=200){return new Promise(t=>{s.style.transition=`opacity ${e}ms ease-out`,s.style.opacity="0",setTimeout(t,e)})}async function Rt(s,e,t){e&&(await Ot(e,150),e.remove()),t.style.display="",t.style.visibility="visible",t.style.opacity="0",await Pt(t,150)}class ne{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 n=this.createPropContext();this.props=_e(t,this.options.props,n);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(),st(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,n){return this.render(t,n)}async close(){this.destroyed||(this.event.emit(m.CLOSE),await this.destroy())}async focus(){this.context===p.IFRAME&&this.iframe?dt(this.iframe):this.context===p.POPUP&&this.hostWindow&>(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&&_t(this.hostWindow,e),this.event.emit(m.RESIZE,e),this.callPropCallback("onResize",e)}async show(){this.context===p.IFRAME&&this.iframe&&ut(this.iframe)}async hide(){this.context===p.IFRAME&&this.iframe&&Re(this.iframe)}async updateProps(e){const t=this.createPropContext(),n=_e({...this.props,...e},this.options.props,t);this.options.validate?.({props:n});const i=this.resolveUrl(n),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=n,this.rendered||this.syncTrustedDomainForUrl(i),this.hostWindow&&!me(this.hostWindow)){const o=this.openedHostDomain??this.getHostDomain(),a=Ee(n,this.options.props,o,pe(this.hostWindow)),c=Pe(a,this.options.props,this.bridge);await this.messenger.send(this.hostWindow,o,u.PROPS,c)}this.event.emit(m.PROPS,this.props),this.callPropCallback("onProps",this.props)}clone(){return new ne(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 n=this.dynamicUrlTrustedOrigin;n&&n!==t&&!this.isExplicitDomainTrust(n)&&this.messenger.removeTrustedDomain(n),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??bt,t=this.options.containerTemplate??Et,n=this.resolveDimensions(),i=this.props.cspNonce;if(this.context===p.IFRAME){const c=this.buildWindowName();this.iframe=this.createIframeElement(c),Re(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);if(this.context===p.IFRAME){if(!this.iframe)throw new Error("Iframe not created during prerender");this.iframe.src=t,this.hostWindow=this.iframe.contentWindow}else{const n=this.buildWindowName();this.hostWindow=pt({url:t,name:n,dimensions:this.resolveDimensions()});const i=wt(this.hostWindow,()=>{this.destroy()});this.cleanup.register(i)}this.hostWindow&&Ge(this.uid,this.hostWindow)}buildUrl(e=this.resolveUrl()){const n=nt(this.props,this.options.props).toString();if(!n)return e;const i=e.includes("?")?"&":"?";return`${e}${i}${n}`}buildWindowName(){const e=this.getHostDomain(),t=Ee(this.props,this.options.props,e,!1),n=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:n,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[n,i]of Object.entries(e)){const r=Dt(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:u.INIT,close:u.CLOSE,resize:u.RESIZE,show:u.SHOW,hide:u.HIDE,onError:u.ERROR,updateProps:u.PROPS,export:u.EXPORT}}getHostDomain(){return this.openedHostDomain?this.openedHostDomain:this.resolveUrlOrigin(this.resolveUrl())??"*"}async waitForHost(){this.initPromise=he();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(u.INIT,()=>(this.initPromise&&this.initPromise.resolve(),{success:!0})),this.messenger.on(u.CLOSE,async()=>(await this.close(),{success:!0})),this.messenger.on(u.RESIZE,async e=>(await this.resize(e),{success:!0})),this.messenger.on(u.FOCUS,async()=>(await this.focus(),{success:!0})),this.messenger.on(u.SHOW,async()=>(await this.show(),{success:!0})),this.messenger.on(u.HIDE,async()=>(await this.hide(),{success:!0})),this.messenger.on(u.ERROR,async e=>{const t=new Error(e.message);return t.stack=e.stack,this.handleError(t),{success:!0}}),this.messenger.on(u.EXPORT,async e=>(this.exports=e,{success:!0})),this.messenger.on(u.CONSUMER_EXPORT,async e=>(this.consumerExports=e,{success:!0})),this.messenger.on(u.GET_SIBLINGS,async e=>{const t=[],n=It(e.tag);if(n)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(),Qe(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.iframe&&(lt(this.iframe),this.iframe=null),this.context===p.POPUP&&this.hostWindow&&mt(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 St{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 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&&!k(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: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,u.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,u.CLOSE,{})}async focus(){window.focus(),await this.messenger.send(this.consumerWindow,this.consumerDomain,u.FOCUS,{})}async resize(e){await this.messenger.send(this.consumerWindow,this.consumerDomain,u.RESIZE,e)}async show(){await this.messenger.send(this.consumerWindow,this.consumerDomain,u.SHOW,{})}async hide(){await this.messenger.send(this.consumerWindow,this.consumerDomain,u.HIDE,{})}onProps(e){return this.propsHandlers.add(e),{cancel:()=>this.propsHandlers.delete(e)}}async onError(e){await this.messenger.send(this.consumerWindow,this.consumerDomain,u.ERROR,{message:e.message,stack:e.stack})}async exportData(e){await this.messenger.send(this.consumerWindow,this.consumerDomain,u.EXPORT,e)}async consumerExport(e){await this.messenger.send(this.consumerWindow,this.consumerDomain,u.CONSUMER_EXPORT,e)}async getPeerInstances(e){return await this.messenger.send(this.consumerWindow,this.consumerDomain,u.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]=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 "${n}":`,r)}return Object.keys(t).length>0?t:void 0}setupMessageHandlers(){this.messenger.on(u.PROPS,e=>{try{const t=Oe(e,this.propDefinitions,this.messenger,this.bridge,this.consumerWindow,this.consumerDomain);Object.assign(this.hostProps,t);for(const n of this.propsHandlers)try{n(t)}catch(i){console.error("Error in props handler:",i)}return this.event.emit(m.PROPS,t),{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}})}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 B(s,e,t={}){if(w){if(e){const i=w.getProps().getConsumerDomain();if(!k(e,i))throw vt(),new Error(`Consumer domain "${i}" is not allowed for this host component`)}return t.deferInit||w.flushInit(),w}if(!ee())return null;const n=Ze();return n?(w=new St(n,s,e,t.deferInit??!1),w):(console.error("Failed to parse ForgeFrame payload from window.name"),null)}function Se(){return ee()}function ve(){return ee()}function Ce(){return window.hostProps}function vt(){w&&(w.destroy(),w=null),delete window.hostProps}const D=new Map,Ie=Symbol("forgeframe.component.options");function Ct(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(D.has(s.tag))throw new Error(`Component "${s.tag}" is already registered`)}function ie(s){Ct(s);const e=[];let t;if(te(s.tag)){const i=B(s.props,s.allowedConsumerDomains);i&&(t=i.hostProps)}const n=function(i={}){const r=new ne(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=()=>te(s.tag),n.isEmbedded=()=>te(s.tag),n.hostProps=t,n[Ie]=s,n.canRenderTo=async i=>{try{return!!(pe(i)||s.domain)}catch{return!1}},D.set(s.tag,n),n}function It(s){return D.get(s)}function Dt(s){return s[Ie]}async function De(s){await s.close()}async function re(s){const e=D.get(s);if(!e)return;const t=[...e.instances];await Promise.all(t.map(n=>n.close()))}async function xe(){const s=Array.from(D.keys());await Promise.all(s.map(e=>re(e)))}function xt(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 Te(s,e){const{React:t}=e,{createElement:n,useRef:i,useEffect:r,useState:o,forwardRef:a}=t,c=a(function(b,P){const{onRendered:S,onError:x,onClose:Fe,context:Nt,className:$e,style:ke,...oe}=b,V=i(null),ae=i(null),ce=i(null),[Le,Ft]=o(null);return r(()=>{const T=V.current;if(!T)return;const y=s(oe);return ae.current=y,S&&y.event.once("rendered",S),Fe&&y.event.once("close",Fe),x&&y.event.on("error",x),y.render(T,Nt).catch(N=>{Ft(N),x?.(N)}),()=>{y.close().catch(()=>{}),ae.current=null,ce.current=null}},[]),r(()=>{const T=ae.current;if(!T)return;const y=oe,N=ce.current;N&&xt(N,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?n("div",{className:$e,style:{color:"red",padding:"16px",...ke}},`Error: ${Le.message}`):n("div",{ref:V,className:$e,style:{display:"inline-block",...ke}})}),l=`ForgeFrame(${s.name||"Component"})`;return c.displayName=l,c}function Tt(s){return function(t){return Te(t,{React:s})}}B(void 0,void 0,{deferInit:!0});const Ne={create:ie,destroy:De,destroyByTag:re,destroyAll:xe,isHost:Se,isEmbedded:ve,getHostProps:Ce,initHost:B,PROP_SERIALIZATION:v,CONTEXT:p,EVENT:m,PopupOpenError:se,VERSION:q,isStandardSchema:E,prop:f};h.AnySchema=z,h.ArraySchema=C,h.BooleanSchema=A,h.CONTEXT=p,h.EVENT=m,h.EnumSchema=W,h.ForgeFrame=Ne,h.FunctionSchema=U,h.LiteralSchema=M,h.NumberSchema=H,h.ObjectSchema=I,h.PROP_SERIALIZATION=v,h.PopupOpenError=se,h.PropSchema=g,h.StringSchema=L,h.VERSION=q,h.create=ie,h.createReactComponent=Te,h.default=Ne,h.destroy=De,h.destroyAll=xe,h.destroyByTag=re,h.getHostProps=Ce,h.initHost=B,h.isEmbedded=ve,h.isHost=Se,h.isStandardSchema=E,h.prop=f,h.withReactComponent=Tt,Object.defineProperties(h,{__esModule:{value:!0},[Symbol.toStringTag]:{value:"Module"}})}));
|
|
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"}})}));
|
package/dist/index.d.ts
CHANGED
package/dist/props/index.d.ts
CHANGED
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
* deserialization for cross-domain component communication.
|
|
8
8
|
*/
|
|
9
9
|
export { BUILTIN_PROP_DEFINITIONS, type BuiltinProps, } from './definitions';
|
|
10
|
-
export { normalizeProps, validateProps, getPropsForHost, propsToQueryParams, } from './normalize';
|
|
10
|
+
export { normalizeProps, validateProps, getPropsForHost, propsToQueryParams, propsToBodyParams, } from './normalize';
|
|
11
11
|
export { serializeProps, deserializeProps, cloneProps, } from './serialize';
|
|
12
12
|
export { isStandardSchema, validateWithSchema, type StandardSchemaV1, type StandardSchemaV1Props, type StandardSchemaV1Result, type StandardSchemaV1SuccessResult, type StandardSchemaV1FailureResult, type StandardSchemaV1Issue, type StandardSchemaV1PathSegment, type StandardSchemaV1Types, type InferInput, type InferOutput, } from './schema';
|
|
13
13
|
export { prop, PropSchema, StringSchema, NumberSchema, BooleanSchema, FunctionSchema, ArraySchema, ObjectSchema, LiteralSchema, EnumSchema, AnySchema, type Prop, type InferObjectShape, } from './prop';
|
|
@@ -57,3 +57,14 @@ export declare function getPropsForHost<P extends Record<string, unknown>>(props
|
|
|
57
57
|
* @public
|
|
58
58
|
*/
|
|
59
59
|
export declare function propsToQueryParams<P extends Record<string, unknown>>(props: P, definitions: PropsDefinition<P>): URLSearchParams;
|
|
60
|
+
/**
|
|
61
|
+
* Builds POST body parameters from props with bodyParam option.
|
|
62
|
+
*
|
|
63
|
+
* @typeParam P - The props type
|
|
64
|
+
* @param props - Props to convert
|
|
65
|
+
* @param definitions - Prop definitions
|
|
66
|
+
* @returns URLSearchParams with body parameters
|
|
67
|
+
*
|
|
68
|
+
* @public
|
|
69
|
+
*/
|
|
70
|
+
export declare function propsToBodyParams<P extends Record<string, unknown>>(props: P, definitions: PropsDefinition<P>): URLSearchParams;
|
package/dist/props/schema.d.ts
CHANGED
|
@@ -47,7 +47,7 @@ export interface StandardSchemaV1Props<Input = unknown, Output = Input> {
|
|
|
47
47
|
/** The name of the schema library (e.g., "zod", "valibot", "arktype") */
|
|
48
48
|
readonly vendor: string;
|
|
49
49
|
/** Optional type metadata for input and output types */
|
|
50
|
-
readonly types?: StandardSchemaV1Types<Input, Output
|
|
50
|
+
readonly types?: StandardSchemaV1Types<Input, Output> | undefined;
|
|
51
51
|
/** Validates an unknown value and returns a result */
|
|
52
52
|
readonly validate: (value: unknown) => StandardSchemaV1Result<Output> | Promise<StandardSchemaV1Result<Output>>;
|
|
53
53
|
}
|
|
@@ -98,6 +98,8 @@ export interface StandardSchemaV1SuccessResult<Output> {
|
|
|
98
98
|
export interface StandardSchemaV1FailureResult {
|
|
99
99
|
/** Array of validation issues */
|
|
100
100
|
readonly issues: ReadonlyArray<StandardSchemaV1Issue>;
|
|
101
|
+
/** Undefined on failure */
|
|
102
|
+
readonly value?: undefined;
|
|
101
103
|
}
|
|
102
104
|
/**
|
|
103
105
|
* A validation issue from a Standard Schema.
|
|
@@ -109,6 +111,10 @@ export interface StandardSchemaV1Issue {
|
|
|
109
111
|
readonly message: string;
|
|
110
112
|
/** Path to the invalid value (for nested objects/arrays) */
|
|
111
113
|
readonly path?: ReadonlyArray<PropertyKey | StandardSchemaV1PathSegment>;
|
|
114
|
+
/** Optional machine-readable error code */
|
|
115
|
+
readonly code?: string | undefined;
|
|
116
|
+
/** The input value that caused this issue */
|
|
117
|
+
readonly input?: unknown;
|
|
112
118
|
}
|
|
113
119
|
/**
|
|
114
120
|
* A path segment for nested validation issues.
|
|
@@ -116,8 +122,10 @@ export interface StandardSchemaV1Issue {
|
|
|
116
122
|
* @public
|
|
117
123
|
*/
|
|
118
124
|
export interface StandardSchemaV1PathSegment {
|
|
119
|
-
/** The property key
|
|
120
|
-
readonly key
|
|
125
|
+
/** The property key in an object path segment */
|
|
126
|
+
readonly key?: PropertyKey | undefined;
|
|
127
|
+
/** The numeric index in an array path segment */
|
|
128
|
+
readonly index?: number | undefined;
|
|
121
129
|
}
|
|
122
130
|
/**
|
|
123
131
|
* Infers the input type from a Standard Schema.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "forgeframe",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.3",
|
|
4
4
|
"description": "Modern cross-domain iframe component framework - a TypeScript alternative to zoid",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"module": "./dist/forgeframe.js",
|
|
@@ -20,7 +20,8 @@
|
|
|
20
20
|
"scripts": {
|
|
21
21
|
"clean": "rm -rf dist",
|
|
22
22
|
"build": "vite build && tsc --emitDeclarationOnly",
|
|
23
|
-
"typecheck": "tsc --noEmit",
|
|
23
|
+
"typecheck": "tsc --noEmit && npm run typecheck:tests",
|
|
24
|
+
"typecheck:tests": "tsc -p tests/typecheck/tsconfig.json --noEmit",
|
|
24
25
|
"test": "vitest",
|
|
25
26
|
"test:run": "vitest run",
|
|
26
27
|
"test:coverage": "vitest run --coverage"
|
|
@@ -43,5 +44,9 @@
|
|
|
43
44
|
"repository": {
|
|
44
45
|
"type": "git",
|
|
45
46
|
"url": "git+https://github.com/jshsmth/ForgeFrame.git"
|
|
47
|
+
},
|
|
48
|
+
"devDependencies": {
|
|
49
|
+
"valibot": "^1.2.0",
|
|
50
|
+
"zod": "^4.3.6"
|
|
46
51
|
}
|
|
47
52
|
}
|