@opensea/vessel
Advanced tools
Comparing version 0.0.5 to 0.0.6
var O = Object.defineProperty; | ||
var C = (s, e, t) => e in s ? O(s, e, { enumerable: !0, configurable: !0, writable: !0, value: t }) : s[e] = t; | ||
var r = (s, e, t) => (C(s, typeof e != "symbol" ? e + "" : e, t), t); | ||
const c = /* @__PURE__ */ new Map(); | ||
function k(s, e, t) { | ||
const n = c.get(s); | ||
var I = (s, e, t) => e in s ? O(s, e, { enumerable: !0, configurable: !0, writable: !0, value: t }) : s[e] = t; | ||
var i = (s, e, t) => (I(s, typeof e != "symbol" ? e + "" : e, t), t); | ||
const d = /* @__PURE__ */ new Map(); | ||
function g(s, e, t) { | ||
const n = d.get(s); | ||
n && (n.timeout && clearTimeout(n.timeout), n.reject( | ||
new Error("[Vessel] Same action fired again. Keeping only latest.") | ||
), c.delete(s)), c.set(s, e), typeof t == "number" && t > 0 && (e.timeout = setTimeout(() => { | ||
u(s), e.reject(new Error("[Vessel] Action timed out.")); | ||
), d.delete(s)), d.set(s, e), typeof t == "number" && t > 0 && (e.timeout = setTimeout(() => { | ||
f(s), e.reject(new Error("[Vessel] Action timed out.")); | ||
}, t)); | ||
} | ||
function u(s) { | ||
const e = c.get(s); | ||
e && (e.timeout && clearTimeout(e.timeout), c.delete(s)); | ||
function f(s) { | ||
const e = d.get(s); | ||
e && (e.timeout && clearTimeout(e.timeout), d.delete(s)); | ||
} | ||
function M(s) { | ||
return c.get(s); | ||
function w(s) { | ||
return d.get(s); | ||
} | ||
@@ -23,3 +23,3 @@ function b(s) { | ||
} | ||
function f(s = "log", e) { | ||
function u(s = "log", e) { | ||
const t = b(e == null ? void 0 : e.scope); | ||
@@ -30,10 +30,10 @@ return (...n) => { | ||
} | ||
function m(s) { | ||
function R(s) { | ||
return { | ||
info: f("info", s), | ||
warn: f("warn", s), | ||
error: f("error", s) | ||
info: u("info", s), | ||
warn: u("warn", s), | ||
error: u("error", s) | ||
}; | ||
} | ||
class d extends Error { | ||
class c extends Error { | ||
constructor(e, t) { | ||
@@ -44,23 +44,23 @@ super(`${b(t)} ${e}`), this.name = "VesselError"; | ||
let E = (s = 21) => crypto.getRandomValues(new Uint8Array(s)).reduce((e, t) => (t &= 63, t < 36 ? e += t.toString(36) : t < 62 ? e += (t - 26).toString(36).toUpperCase() : t > 62 ? e += "-" : e += "_", e), ""); | ||
const g = "application/x-opensea-vessel-v1+json"; | ||
function I() { | ||
const v = "application/x-opensea-vessel-v1+json"; | ||
function C() { | ||
return `op-${E()}`; | ||
} | ||
function $() { | ||
function H() { | ||
return `msg-${E()}`; | ||
} | ||
function H(s) { | ||
function $(s) { | ||
const e = new URL(s); | ||
return `${e.protocol}//${e.host}`; | ||
} | ||
var i = /* @__PURE__ */ ((s) => (s.Handshake = "handshake", s.HandshakeReply = "handshake-reply", s.Message = "message", s.MessageReply = "message-reply", s.Close = "close", s))(i || {}); | ||
var r = /* @__PURE__ */ ((s) => (s.Handshake = "handshake", s.HandshakeReply = "handshake-reply", s.Message = "message", s.MessageReply = "message-reply", s.Close = "close", s))(r || {}); | ||
class h { | ||
constructor(e, t) { | ||
r(this, "type"); | ||
r(this, "id"); | ||
r(this, "from"); | ||
r(this, "operation"); | ||
r(this, "payload"); | ||
r(this, "metadata"); | ||
this.type = e, this.id = (t == null ? void 0 : t.id) ?? $(), this.from = (t == null ? void 0 : t.from) ?? g, this.operation = (t == null ? void 0 : t.operation) ?? I(), this.payload = t == null ? void 0 : t.payload, this.metadata = t == null ? void 0 : t.metadata; | ||
i(this, "type"); | ||
i(this, "id"); | ||
i(this, "from"); | ||
i(this, "operation"); | ||
i(this, "payload"); | ||
i(this, "metadata"); | ||
this.type = e, this.id = (t == null ? void 0 : t.id) ?? H(), this.from = (t == null ? void 0 : t.from) ?? v, this.operation = (t == null ? void 0 : t.operation) ?? C(), this.payload = t == null ? void 0 : t.payload, this.metadata = t == null ? void 0 : t.metadata; | ||
} | ||
@@ -78,4 +78,4 @@ toObject() { | ||
} | ||
function S(s, e = g) { | ||
if (s && typeof s == "object" && "from" in s && s.from === e && "type" in s && Object.values(i).includes(s.type) && "id" in s && "operation" in s) | ||
function A(s, e = v) { | ||
if (s && typeof s == "object" && "from" in s && s.from === e && "type" in s && Object.values(r).includes(s.type) && "id" in s && "operation" in s) | ||
return new h(s.type, { | ||
@@ -88,24 +88,24 @@ id: s.id, | ||
}); | ||
throw new d("Invalid message."); | ||
throw new c("Invalid message."); | ||
} | ||
function R(s, e, t) { | ||
function m(s, e, t) { | ||
if (e === "*" || s.origin === e) | ||
return S(s.data, t ?? g); | ||
throw new d("Invalid message event origin."); | ||
return A(s.data, t ?? v); | ||
throw new c("Invalid message event origin."); | ||
} | ||
class w { | ||
class y { | ||
constructor(e) { | ||
r(this, "log"); | ||
r(this, "parent", window.parent); | ||
r(this, "subscriptions"); | ||
r(this, "closed", !1); | ||
r(this, "parentOrigin"); | ||
i(this, "log"); | ||
i(this, "parent", window.parent); | ||
i(this, "subscriptions"); | ||
i(this, "closed", !1); | ||
i(this, "parentOrigin"); | ||
this.parentOrigin = e.parentOrigin, this.subscriptions = { | ||
[i.Message]: [], | ||
[i.Close]: [] | ||
}, this.log = m({ debug: e.debug, scope: "CHILD" }), this.onMessage = this.onMessage.bind(this), window.addEventListener("message", this.onMessage, !1); | ||
[r.Message]: [], | ||
[r.Close]: [] | ||
}, this.log = R({ debug: e.debug, scope: "CHILD" }), this.onMessage = this.onMessage.bind(this), window.addEventListener("message", this.onMessage, !1); | ||
} | ||
callSubscriptions(e, t) { | ||
const n = this.sendMessage.bind(this); | ||
if (e === i.Close) { | ||
if (e === r.Close) { | ||
this.close(), this.subscriptions[e].forEach((o) => o(t)); | ||
@@ -117,3 +117,3 @@ return; | ||
n( | ||
new h(i.MessageReply, { | ||
new h(r.MessageReply, { | ||
operation: t.operation, | ||
@@ -129,11 +129,11 @@ payload: a, | ||
try { | ||
const t = R(e, this.parentOrigin); | ||
const t = m(e, this.parentOrigin); | ||
switch (t.type) { | ||
case i.Handshake: | ||
case r.Handshake: | ||
this.sendHandshakeReply(t); | ||
return; | ||
case i.Message: | ||
this.callSubscriptions(i.Message, t); | ||
case r.Message: | ||
this.callSubscriptions(r.Message, t); | ||
return; | ||
case i.MessageReply: | ||
case r.MessageReply: | ||
this.resolveReply(t); | ||
@@ -148,3 +148,3 @@ return; | ||
this.log.info(`Received handshake from ${this.parentOrigin}`); | ||
const t = new h(i.HandshakeReply, { | ||
const t = new h(r.HandshakeReply, { | ||
operation: e.operation | ||
@@ -162,7 +162,7 @@ }); | ||
try { | ||
const n = M(e.operation); | ||
const n = w(e.operation); | ||
if (n) { | ||
this.log.info(`Resolving action ${e.operation}`); | ||
const { resolve: o, reject: a } = n; | ||
if (u(e.operation), (t = e.payload) != null && t.error) { | ||
if (f(e.operation), (t = e.payload) != null && t.error) { | ||
a(e.payload.error); | ||
@@ -179,5 +179,5 @@ return; | ||
if (!this.parent) | ||
throw new d("Parent frame not found."); | ||
throw new c("Parent frame not found."); | ||
if (this.closed) | ||
throw new d("Cannot send message, frame is closed."); | ||
throw new c("Cannot send message, frame is closed."); | ||
this.parent.postMessage(e.toObject(), this.parentOrigin); | ||
@@ -195,7 +195,7 @@ } | ||
return new Promise((o, a) => { | ||
const l = new h(i.Message, { | ||
const l = new h(r.Message, { | ||
payload: e, | ||
metadata: t | ||
}); | ||
k(l.operation, { resolve: o, reject: a }, n); | ||
g(l.operation, { resolve: o, reject: a }, n); | ||
try { | ||
@@ -214,3 +214,3 @@ this.sendMessage(l); | ||
emit(e, t) { | ||
const n = new h(i.Message, { | ||
const n = new h(r.Message, { | ||
payload: e, | ||
@@ -236,22 +236,23 @@ metadata: t | ||
this.closed = !0, window.removeEventListener("message", this.onMessage); | ||
const e = new h(i.Close); | ||
const e = new h(r.Close); | ||
this.parent.postMessage(e.toObject(), this.parentOrigin); | ||
} | ||
} | ||
class y { | ||
class k { | ||
constructor(e) { | ||
r(this, "parentFrame"); | ||
r(this, "child"); | ||
r(this, "frame"); | ||
r(this, "handshakeOperation"); | ||
r(this, "handshakeInterval"); | ||
r(this, "subscriptions"); | ||
r(this, "log"); | ||
r(this, "childOrigin"); | ||
r(this, "childUrl"); | ||
r(this, "connected"); | ||
this.frame = e.frame, this.parentFrame = window, this.child = null, this.childUrl = e.url, this.childOrigin = H(e.url), this.connected = !1, this.handshakeOperation = null, this.handshakeInterval = null, this.subscriptions = { | ||
[i.Message]: [], | ||
[i.Close]: [] | ||
}, this.log = m({ debug: e.debug, scope: "PARENT" }), this.parentFrame.addEventListener( | ||
i(this, "parentFrame"); | ||
i(this, "child"); | ||
i(this, "frame"); | ||
i(this, "handshakeOperation"); | ||
i(this, "handshakeInterval"); | ||
i(this, "handshakeActionResolverId", "HANDSHAKE"); | ||
i(this, "subscriptions"); | ||
i(this, "log"); | ||
i(this, "childOrigin"); | ||
i(this, "childUrl"); | ||
i(this, "connected"); | ||
this.frame = e.frame, this.parentFrame = window, this.child = null, this.childUrl = e.url, this.childOrigin = $(e.url), this.connected = !1, this.handshakeOperation = null, this.handshakeInterval = null, this.subscriptions = { | ||
[r.Message]: [], | ||
[r.Close]: [] | ||
}, this.log = R({ debug: e.debug, scope: "PARENT" }), this.parentFrame.addEventListener( | ||
"message", | ||
@@ -269,3 +270,3 @@ this.onMessage.bind(this), | ||
e += 1; | ||
const n = new h(i.Handshake); | ||
const n = new h(r.Handshake); | ||
this.handshakeOperation = n.operation, this.sendMessage(n); | ||
@@ -284,3 +285,3 @@ } | ||
const n = this.sendMessage.bind(this); | ||
if (e === i.Close) { | ||
if (e === r.Close) { | ||
this.close(), this.subscriptions[e].forEach((o) => o(t)); | ||
@@ -292,3 +293,3 @@ return; | ||
n( | ||
new h(i.MessageReply, { | ||
new h(r.MessageReply, { | ||
operation: t.operation, | ||
@@ -303,21 +304,32 @@ payload: a, | ||
handleHandshakeReply(e) { | ||
var a; | ||
if (this.log.info(`Received handshake reply from ${this.childUrl}`), e.operation !== this.handshakeOperation) | ||
throw new Error("Handshake reply operation does not match"); | ||
this.handshakeInterval && window.clearInterval(this.handshakeInterval), this.connected = !0; | ||
const t = w(this.handshakeActionResolverId); | ||
if (!t) | ||
throw new c("Handshake resolver not found"); | ||
this.log.info(`Resolving action ${this.handshakeActionResolverId}`); | ||
const { resolve: n, reject: o } = t; | ||
if (f(this.handshakeActionResolverId), (a = e.payload) != null && a.error) { | ||
o(e.payload.error); | ||
return; | ||
} | ||
n(e.payload); | ||
} | ||
onMessage(e) { | ||
try { | ||
const t = R(e, this.childOrigin); | ||
const t = m(e, this.childOrigin); | ||
switch (t.type) { | ||
case i.HandshakeReply: | ||
case r.HandshakeReply: | ||
this.handleHandshakeReply(t); | ||
break; | ||
case i.Message: | ||
this.callSubscriptions(i.Message, t); | ||
case r.Message: | ||
this.callSubscriptions(r.Message, t); | ||
break; | ||
case i.MessageReply: | ||
case r.MessageReply: | ||
this.resolveReply(t); | ||
break; | ||
case i.Close: | ||
this.callSubscriptions(i.Close, t); | ||
case r.Close: | ||
this.callSubscriptions(r.Close, t); | ||
break; | ||
@@ -335,7 +347,7 @@ default: | ||
try { | ||
const n = M(e.operation); | ||
const n = w(e.operation); | ||
if (n) { | ||
this.log.info(`Resolving action ${e.operation}`); | ||
const { resolve: o, reject: a } = n; | ||
if (u(e.operation), (t = e.payload) != null && t.error) { | ||
if (f(e.operation), (t = e.payload) != null && t.error) { | ||
a(e.payload.error); | ||
@@ -352,5 +364,5 @@ return; | ||
if (!this.child) | ||
throw new d("Child frame not found."); | ||
if (e.type !== i.Handshake && !this.connected) | ||
throw new d("Not connected to child frame."); | ||
throw new c("Child frame not found."); | ||
if (e.type !== r.Handshake && !this.connected) | ||
throw new c("Not connected to child frame."); | ||
this.child.postMessage(e.toObject(), this.childOrigin); | ||
@@ -368,7 +380,7 @@ } | ||
return new Promise((o, a) => { | ||
const l = new h(i.Message, { | ||
const l = new h(r.Message, { | ||
payload: e, | ||
metadata: t | ||
}); | ||
k(l.operation, { resolve: o, reject: a }, n); | ||
g(l.operation, { resolve: o, reject: a }, n); | ||
try { | ||
@@ -387,3 +399,3 @@ this.sendMessage(l); | ||
emit(e, t) { | ||
const n = new h(i.Message, { | ||
const n = new h(r.Message, { | ||
payload: e, | ||
@@ -395,3 +407,5 @@ metadata: t | ||
connect() { | ||
this.log.info(`Connecting to child iframe ${this.childUrl}`), this.frame.addEventListener("load", this.startHandshake.bind(this)), this.frame.src = this.childUrl; | ||
return new Promise((e, t) => { | ||
this.log.info(`Connecting to child iframe ${this.childUrl}`), this.frame.addEventListener("load", this.startHandshake.bind(this)), this.frame.src = this.childUrl, g(this.handshakeActionResolverId, { resolve: e, reject: t }); | ||
}); | ||
} | ||
@@ -413,19 +427,19 @@ on(e, t) { | ||
var e; | ||
this.log.info("Closing frames"), this.subscriptions[i.Message] = [], this.subscriptions[i.Close] = [], this.handshakeOperation = null, this.handshakeInterval && window.clearInterval(this.handshakeInterval), this.connected = !1, (e = this.frame.parentElement) == null || e.removeChild(this.frame); | ||
this.log.info("Closing frames"), this.subscriptions[r.Message] = [], this.subscriptions[r.Close] = [], this.handshakeOperation = null, this.handshakeInterval && window.clearInterval(this.handshakeInterval), this.connected = !1, (e = this.frame.parentElement) == null || e.removeChild(this.frame); | ||
} | ||
} | ||
class v { | ||
class M { | ||
static createParentFrame(e) { | ||
return new y(e); | ||
return new k(e); | ||
} | ||
static createChildFrame(e) { | ||
return new w(e); | ||
return new y(e); | ||
} | ||
} | ||
r(v, "ParentFrame", y), r(v, "ChildFrame", w); | ||
i(M, "ParentFrame", k), i(M, "ChildFrame", y); | ||
export { | ||
w as ChildFrame, | ||
i as MessageType, | ||
y as ParentFrame, | ||
v as Vessel | ||
y as ChildFrame, | ||
r as MessageType, | ||
k as ParentFrame, | ||
M as Vessel | ||
}; |
@@ -1,1 +0,1 @@ | ||
(function(h,a){typeof exports=="object"&&typeof module<"u"?a(exports):typeof define=="function"&&define.amd?define(["exports"],a):(h=typeof globalThis<"u"?globalThis:h||self,a(h.Vessel={}))})(this,function(h){"use strict";var H=Object.defineProperty;var F=(h,a,p)=>a in h?H(h,a,{enumerable:!0,configurable:!0,writable:!0,value:p}):h[a]=p;var r=(h,a,p)=>(F(h,typeof a!="symbol"?a+"":a,p),p);const a=new Map;function p(s,e,t){const n=a.get(s);n&&(n.timeout&&clearTimeout(n.timeout),n.reject(new Error("[Vessel] Same action fired again. Keeping only latest.")),a.delete(s)),a.set(s,e),typeof t=="number"&&t>0&&(e.timeout=setTimeout(()=>{g(s),e.reject(new Error("[Vessel] Action timed out."))},t))}function g(s){const e=a.get(s);e&&(e.timeout&&clearTimeout(e.timeout),a.delete(s))}function b(s){return a.get(s)}function m(s){return`${s?`[VESSEL-${s.toUpperCase()}]:`:"[VESSEL]:"}`.trimStart()}function w(s="log",e){const t=m(e==null?void 0:e.scope);return(...n)=>{e!=null&&e.debug&&console[s](t,...n)}}function E(s){return{info:w("info",s),warn:w("warn",s),error:w("error",s)}}class f extends Error{constructor(e,t){super(`${m(t)} ${e}`),this.name="VesselError"}}let R=(s=21)=>crypto.getRandomValues(new Uint8Array(s)).reduce((e,t)=>(t&=63,t<36?e+=t.toString(36):t<62?e+=(t-26).toString(36).toUpperCase():t>62?e+="-":e+="_",e),"");const y="application/x-opensea-vessel-v1+json";function C(){return`op-${R()}`}function I(){return`msg-${R()}`}function S(s){const e=new URL(s);return`${e.protocol}//${e.host}`}var i=(s=>(s.Handshake="handshake",s.HandshakeReply="handshake-reply",s.Message="message",s.MessageReply="message-reply",s.Close="close",s))(i||{});class c{constructor(e,t){r(this,"type");r(this,"id");r(this,"from");r(this,"operation");r(this,"payload");r(this,"metadata");this.type=e,this.id=(t==null?void 0:t.id)??I(),this.from=(t==null?void 0:t.from)??y,this.operation=(t==null?void 0:t.operation)??C(),this.payload=t==null?void 0:t.payload,this.metadata=t==null?void 0:t.metadata}toObject(){return{type:this.type,id:this.id,from:this.from,operation:this.operation,payload:this.payload,metadata:this.metadata}}}function $(s,e=y){if(s&&typeof s=="object"&&"from"in s&&s.from===e&&"type"in s&&Object.values(i).includes(s.type)&&"id"in s&&"operation"in s)return new c(s.type,{id:s.id,from:s.from,operation:s.operation,payload:s.payload,metadata:s.metadata});throw new f("Invalid message.")}function O(s,e,t){if(e==="*"||s.origin===e)return $(s.data,t??y);throw new f("Invalid message event origin.")}class v{constructor(e){r(this,"log");r(this,"parent",window.parent);r(this,"subscriptions");r(this,"closed",!1);r(this,"parentOrigin");this.parentOrigin=e.parentOrigin,this.subscriptions={[i.Message]:[],[i.Close]:[]},this.log=E({debug:e.debug,scope:"CHILD"}),this.onMessage=this.onMessage.bind(this),window.addEventListener("message",this.onMessage,!1)}callSubscriptions(e,t){const n=this.sendMessage.bind(this);if(e===i.Close){this.close(),this.subscriptions[e].forEach(o=>o(t));return}this.subscriptions[e].forEach(o=>{o(t,(l,d)=>{n(new c(i.MessageReply,{operation:t.operation,payload:l,metadata:d}))})})}onMessage(e){try{const t=O(e,this.parentOrigin);switch(t.type){case i.Handshake:this.sendHandshakeReply(t);return;case i.Message:this.callSubscriptions(i.Message,t);return;case i.MessageReply:this.resolveReply(t);return}}catch(t){this.log.error("Error parsing message from parent",t)}}sendHandshakeReply(e){this.log.info(`Received handshake from ${this.parentOrigin}`);const t=new c(i.HandshakeReply,{operation:e.operation});try{this.parent.postMessage(t.toObject(),this.parentOrigin)}catch(n){this.log.error("Error sending handshake reply",n)}}resolveReply(e){var t;this.log.info(`Received reply for action ${e.operation}`);try{const n=b(e.operation);if(n){this.log.info(`Resolving action ${e.operation}`);const{resolve:o,reject:l}=n;if(g(e.operation),(t=e.payload)!=null&&t.error){l(e.payload.error);return}o(e.payload)}}catch(n){this.log.error("Error resolving reply",n)}}sendMessage(e){if(!this.parent)throw new f("Parent frame not found.");if(this.closed)throw new f("Cannot send message, frame is closed.");this.parent.postMessage(e.toObject(),this.parentOrigin)}async send(e,t,n=5e3){return new Promise((o,l)=>{const d=new c(i.Message,{payload:e,metadata:t});p(d.operation,{resolve:o,reject:l},n);try{this.sendMessage(d)}catch(u){this.log.error("Error sending message to parent",u),l(u)}})}emit(e,t){const n=new c(i.Message,{payload:e,metadata:t});this.sendMessage(n)}on(e,t){return this.subscriptions[e].push(t),()=>{this.off.call(this,e,t)}}once(e,t){const n=this.on(e,(...o)=>{n(),t(...o)})}off(e,t){this.subscriptions[e]=this.subscriptions[e].filter(n=>n!==t)}close(){this.closed=!0,window.removeEventListener("message",this.onMessage);const e=new c(i.Close);this.parent.postMessage(e.toObject(),this.parentOrigin)}}class M{constructor(e){r(this,"parentFrame");r(this,"child");r(this,"frame");r(this,"handshakeOperation");r(this,"handshakeInterval");r(this,"subscriptions");r(this,"log");r(this,"childOrigin");r(this,"childUrl");r(this,"connected");this.frame=e.frame,this.parentFrame=window,this.child=null,this.childUrl=e.url,this.childOrigin=S(e.url),this.connected=!1,this.handshakeOperation=null,this.handshakeInterval=null,this.subscriptions={[i.Message]:[],[i.Close]:[]},this.log=E({debug:e.debug,scope:"PARENT"}),this.parentFrame.addEventListener("message",this.onMessage.bind(this),!1)}startHandshake(){if(this.log.info(`Sending handshake to ${this.childUrl}`),this.child=this.frame.contentWindow,!this.child)throw new Error("Child frame not found");let e=0;const t=()=>{if(this.log.info("Attempting handshake..."),this.child){e+=1;const n=new c(i.Handshake);this.handshakeOperation=n.operation,this.sendMessage(n)}};t(),this.handshakeInterval=window.setInterval(()=>{if(e>=5){this.log.warn("Handshake failed after 5 attempts. Giving up."),this.handshakeInterval&&window.clearInterval(this.handshakeInterval);return}t()},500)}callSubscriptions(e,t){const n=this.sendMessage.bind(this);if(e===i.Close){this.close(),this.subscriptions[e].forEach(o=>o(t));return}this.subscriptions[e].forEach(o=>{o(t,(l,d)=>{n(new c(i.MessageReply,{operation:t.operation,payload:l,metadata:d}))})})}handleHandshakeReply(e){if(this.log.info(`Received handshake reply from ${this.childUrl}`),e.operation!==this.handshakeOperation)throw new Error("Handshake reply operation does not match");this.handshakeInterval&&window.clearInterval(this.handshakeInterval),this.connected=!0}onMessage(e){try{const t=O(e,this.childOrigin);switch(t.type){case i.HandshakeReply:this.handleHandshakeReply(t);break;case i.Message:this.callSubscriptions(i.Message,t);break;case i.MessageReply:this.resolveReply(t);break;case i.Close:this.callSubscriptions(i.Close,t);break;default:this.log.warn(`Unknown message type: ${t.type}`)}}catch(t){this.log.error("Error handling message",t)}}resolveReply(e){var t;this.log.info(`Received reply for action ${e.operation}`);try{const n=b(e.operation);if(n){this.log.info(`Resolving action ${e.operation}`);const{resolve:o,reject:l}=n;if(g(e.operation),(t=e.payload)!=null&&t.error){l(e.payload.error);return}o(e.payload)}}catch(n){this.log.error("Error resolving reply",n)}}sendMessage(e){if(!this.child)throw new f("Child frame not found.");if(e.type!==i.Handshake&&!this.connected)throw new f("Not connected to child frame.");this.child.postMessage(e.toObject(),this.childOrigin)}async send(e,t,n=5e3){return new Promise((o,l)=>{const d=new c(i.Message,{payload:e,metadata:t});p(d.operation,{resolve:o,reject:l},n);try{this.sendMessage(d)}catch(u){this.log.error("Error sending message to child",u),l(u)}})}emit(e,t){const n=new c(i.Message,{payload:e,metadata:t});this.sendMessage(n)}connect(){this.log.info(`Connecting to child iframe ${this.childUrl}`),this.frame.addEventListener("load",this.startHandshake.bind(this)),this.frame.src=this.childUrl}on(e,t){return this.subscriptions[e].push(t),()=>{this.off.call(this,e,t)}}once(e,t){const n=this.on(e,(...o)=>{n(),t(...o)})}off(e,t){this.subscriptions[e]=this.subscriptions[e].filter(n=>n!==t)}close(){var e;this.log.info("Closing frames"),this.subscriptions[i.Message]=[],this.subscriptions[i.Close]=[],this.handshakeOperation=null,this.handshakeInterval&&window.clearInterval(this.handshakeInterval),this.connected=!1,(e=this.frame.parentElement)==null||e.removeChild(this.frame)}}class k{static createParentFrame(e){return new M(e)}static createChildFrame(e){return new v(e)}}r(k,"ParentFrame",M),r(k,"ChildFrame",v),h.ChildFrame=v,h.MessageType=i,h.ParentFrame=M,h.Vessel=k,Object.defineProperty(h,Symbol.toStringTag,{value:"Module"})}); | ||
(function(l,a){typeof exports=="object"&&typeof module<"u"?a(exports):typeof define=="function"&&define.amd?define(["exports"],a):(l=typeof globalThis<"u"?globalThis:l||self,a(l.Vessel={}))})(this,function(l){"use strict";var $=Object.defineProperty;var A=(l,a,d)=>a in l?$(l,a,{enumerable:!0,configurable:!0,writable:!0,value:d}):l[a]=d;var r=(l,a,d)=>(A(l,typeof a!="symbol"?a+"":a,d),d);const a=new Map;function d(s,e,t){const n=a.get(s);n&&(n.timeout&&clearTimeout(n.timeout),n.reject(new Error("[Vessel] Same action fired again. Keeping only latest.")),a.delete(s)),a.set(s,e),typeof t=="number"&&t>0&&(e.timeout=setTimeout(()=>{g(s),e.reject(new Error("[Vessel] Action timed out."))},t))}function g(s){const e=a.get(s);e&&(e.timeout&&clearTimeout(e.timeout),a.delete(s))}function w(s){return a.get(s)}function R(s){return`${s?`[VESSEL-${s.toUpperCase()}]:`:"[VESSEL]:"}`.trimStart()}function v(s="log",e){const t=R(e==null?void 0:e.scope);return(...n)=>{e!=null&&e.debug&&console[s](t,...n)}}function m(s){return{info:v("info",s),warn:v("warn",s),error:v("error",s)}}class f extends Error{constructor(e,t){super(`${R(t)} ${e}`),this.name="VesselError"}}let E=(s=21)=>crypto.getRandomValues(new Uint8Array(s)).reduce((e,t)=>(t&=63,t<36?e+=t.toString(36):t<62?e+=(t-26).toString(36).toUpperCase():t>62?e+="-":e+="_",e),"");const y="application/x-opensea-vessel-v1+json";function I(){return`op-${E()}`}function C(){return`msg-${E()}`}function H(s){const e=new URL(s);return`${e.protocol}//${e.host}`}var i=(s=>(s.Handshake="handshake",s.HandshakeReply="handshake-reply",s.Message="message",s.MessageReply="message-reply",s.Close="close",s))(i||{});class c{constructor(e,t){r(this,"type");r(this,"id");r(this,"from");r(this,"operation");r(this,"payload");r(this,"metadata");this.type=e,this.id=(t==null?void 0:t.id)??C(),this.from=(t==null?void 0:t.from)??y,this.operation=(t==null?void 0:t.operation)??I(),this.payload=t==null?void 0:t.payload,this.metadata=t==null?void 0:t.metadata}toObject(){return{type:this.type,id:this.id,from:this.from,operation:this.operation,payload:this.payload,metadata:this.metadata}}}function S(s,e=y){if(s&&typeof s=="object"&&"from"in s&&s.from===e&&"type"in s&&Object.values(i).includes(s.type)&&"id"in s&&"operation"in s)return new c(s.type,{id:s.id,from:s.from,operation:s.operation,payload:s.payload,metadata:s.metadata});throw new f("Invalid message.")}function O(s,e,t){if(e==="*"||s.origin===e)return S(s.data,t??y);throw new f("Invalid message event origin.")}class k{constructor(e){r(this,"log");r(this,"parent",window.parent);r(this,"subscriptions");r(this,"closed",!1);r(this,"parentOrigin");this.parentOrigin=e.parentOrigin,this.subscriptions={[i.Message]:[],[i.Close]:[]},this.log=m({debug:e.debug,scope:"CHILD"}),this.onMessage=this.onMessage.bind(this),window.addEventListener("message",this.onMessage,!1)}callSubscriptions(e,t){const n=this.sendMessage.bind(this);if(e===i.Close){this.close(),this.subscriptions[e].forEach(o=>o(t));return}this.subscriptions[e].forEach(o=>{o(t,(h,p)=>{n(new c(i.MessageReply,{operation:t.operation,payload:h,metadata:p}))})})}onMessage(e){try{const t=O(e,this.parentOrigin);switch(t.type){case i.Handshake:this.sendHandshakeReply(t);return;case i.Message:this.callSubscriptions(i.Message,t);return;case i.MessageReply:this.resolveReply(t);return}}catch(t){this.log.error("Error parsing message from parent",t)}}sendHandshakeReply(e){this.log.info(`Received handshake from ${this.parentOrigin}`);const t=new c(i.HandshakeReply,{operation:e.operation});try{this.parent.postMessage(t.toObject(),this.parentOrigin)}catch(n){this.log.error("Error sending handshake reply",n)}}resolveReply(e){var t;this.log.info(`Received reply for action ${e.operation}`);try{const n=w(e.operation);if(n){this.log.info(`Resolving action ${e.operation}`);const{resolve:o,reject:h}=n;if(g(e.operation),(t=e.payload)!=null&&t.error){h(e.payload.error);return}o(e.payload)}}catch(n){this.log.error("Error resolving reply",n)}}sendMessage(e){if(!this.parent)throw new f("Parent frame not found.");if(this.closed)throw new f("Cannot send message, frame is closed.");this.parent.postMessage(e.toObject(),this.parentOrigin)}async send(e,t,n=5e3){return new Promise((o,h)=>{const p=new c(i.Message,{payload:e,metadata:t});d(p.operation,{resolve:o,reject:h},n);try{this.sendMessage(p)}catch(u){this.log.error("Error sending message to parent",u),h(u)}})}emit(e,t){const n=new c(i.Message,{payload:e,metadata:t});this.sendMessage(n)}on(e,t){return this.subscriptions[e].push(t),()=>{this.off.call(this,e,t)}}once(e,t){const n=this.on(e,(...o)=>{n(),t(...o)})}off(e,t){this.subscriptions[e]=this.subscriptions[e].filter(n=>n!==t)}close(){this.closed=!0,window.removeEventListener("message",this.onMessage);const e=new c(i.Close);this.parent.postMessage(e.toObject(),this.parentOrigin)}}class M{constructor(e){r(this,"parentFrame");r(this,"child");r(this,"frame");r(this,"handshakeOperation");r(this,"handshakeInterval");r(this,"handshakeActionResolverId","HANDSHAKE");r(this,"subscriptions");r(this,"log");r(this,"childOrigin");r(this,"childUrl");r(this,"connected");this.frame=e.frame,this.parentFrame=window,this.child=null,this.childUrl=e.url,this.childOrigin=H(e.url),this.connected=!1,this.handshakeOperation=null,this.handshakeInterval=null,this.subscriptions={[i.Message]:[],[i.Close]:[]},this.log=m({debug:e.debug,scope:"PARENT"}),this.parentFrame.addEventListener("message",this.onMessage.bind(this),!1)}startHandshake(){if(this.log.info(`Sending handshake to ${this.childUrl}`),this.child=this.frame.contentWindow,!this.child)throw new Error("Child frame not found");let e=0;const t=()=>{if(this.log.info("Attempting handshake..."),this.child){e+=1;const n=new c(i.Handshake);this.handshakeOperation=n.operation,this.sendMessage(n)}};t(),this.handshakeInterval=window.setInterval(()=>{if(e>=5){this.log.warn("Handshake failed after 5 attempts. Giving up."),this.handshakeInterval&&window.clearInterval(this.handshakeInterval);return}t()},500)}callSubscriptions(e,t){const n=this.sendMessage.bind(this);if(e===i.Close){this.close(),this.subscriptions[e].forEach(o=>o(t));return}this.subscriptions[e].forEach(o=>{o(t,(h,p)=>{n(new c(i.MessageReply,{operation:t.operation,payload:h,metadata:p}))})})}handleHandshakeReply(e){var h;if(this.log.info(`Received handshake reply from ${this.childUrl}`),e.operation!==this.handshakeOperation)throw new Error("Handshake reply operation does not match");this.handshakeInterval&&window.clearInterval(this.handshakeInterval),this.connected=!0;const t=w(this.handshakeActionResolverId);if(!t)throw new f("Handshake resolver not found");this.log.info(`Resolving action ${this.handshakeActionResolverId}`);const{resolve:n,reject:o}=t;if(g(this.handshakeActionResolverId),(h=e.payload)!=null&&h.error){o(e.payload.error);return}n(e.payload)}onMessage(e){try{const t=O(e,this.childOrigin);switch(t.type){case i.HandshakeReply:this.handleHandshakeReply(t);break;case i.Message:this.callSubscriptions(i.Message,t);break;case i.MessageReply:this.resolveReply(t);break;case i.Close:this.callSubscriptions(i.Close,t);break;default:this.log.warn(`Unknown message type: ${t.type}`)}}catch(t){this.log.error("Error handling message",t)}}resolveReply(e){var t;this.log.info(`Received reply for action ${e.operation}`);try{const n=w(e.operation);if(n){this.log.info(`Resolving action ${e.operation}`);const{resolve:o,reject:h}=n;if(g(e.operation),(t=e.payload)!=null&&t.error){h(e.payload.error);return}o(e.payload)}}catch(n){this.log.error("Error resolving reply",n)}}sendMessage(e){if(!this.child)throw new f("Child frame not found.");if(e.type!==i.Handshake&&!this.connected)throw new f("Not connected to child frame.");this.child.postMessage(e.toObject(),this.childOrigin)}async send(e,t,n=5e3){return new Promise((o,h)=>{const p=new c(i.Message,{payload:e,metadata:t});d(p.operation,{resolve:o,reject:h},n);try{this.sendMessage(p)}catch(u){this.log.error("Error sending message to child",u),h(u)}})}emit(e,t){const n=new c(i.Message,{payload:e,metadata:t});this.sendMessage(n)}connect(){return new Promise((e,t)=>{this.log.info(`Connecting to child iframe ${this.childUrl}`),this.frame.addEventListener("load",this.startHandshake.bind(this)),this.frame.src=this.childUrl,d(this.handshakeActionResolverId,{resolve:e,reject:t})})}on(e,t){return this.subscriptions[e].push(t),()=>{this.off.call(this,e,t)}}once(e,t){const n=this.on(e,(...o)=>{n(),t(...o)})}off(e,t){this.subscriptions[e]=this.subscriptions[e].filter(n=>n!==t)}close(){var e;this.log.info("Closing frames"),this.subscriptions[i.Message]=[],this.subscriptions[i.Close]=[],this.handshakeOperation=null,this.handshakeInterval&&window.clearInterval(this.handshakeInterval),this.connected=!1,(e=this.frame.parentElement)==null||e.removeChild(this.frame)}}class b{static createParentFrame(e){return new M(e)}static createChildFrame(e){return new k(e)}}r(b,"ParentFrame",M),r(b,"ChildFrame",k),l.ChildFrame=k,l.MessageType=i,l.ParentFrame=M,l.Vessel=b,Object.defineProperty(l,Symbol.toStringTag,{value:"Module"})}); |
@@ -15,2 +15,3 @@ import { Message, MessageMetadataType, MessagePayloadType, MessageType } from "./utils/messaging"; | ||
private handshakeInterval; | ||
private handshakeActionResolverId; | ||
private subscriptions; | ||
@@ -43,3 +44,3 @@ private log; | ||
emit<P extends MessagePayloadType, M extends MessageMetadataType>(payload: P, metadata?: M): void; | ||
connect(): void; | ||
connect(): Promise<void>; | ||
on(type: MessageType.Message, fn: ActionSubscriptionFn): () => void; | ||
@@ -46,0 +47,0 @@ once(type: MessageType.Message, fn: ActionSubscriptionFn): void; |
{ | ||
"name": "@opensea/vessel", | ||
"version": "0.0.5", | ||
"version": "0.0.6", | ||
"description": "🚢 Vessel: a promise-based postMessage library that sails your data smoothly across the Opensea.", | ||
@@ -5,0 +5,0 @@ "files": [ |
@@ -100,2 +100,12 @@ import { ParentFrame } from "./parent" | ||
test.only("It should resolve connect promise after a successful handshake", async () => { | ||
const parentFrame = new ParentFrame({ frame, url }) | ||
const connectPromise = parentFrame.connect() | ||
doHandshake(parentFrame.childOrigin) | ||
await connectPromise | ||
expect(parentFrame.connected).toBe(true) | ||
}) | ||
test("It should handle subscription messages", () => { | ||
@@ -102,0 +112,0 @@ const parentFrame = new ParentFrame({ frame, url }) |
@@ -36,2 +36,3 @@ import { | ||
private handshakeInterval: number | null | ||
private handshakeActionResolverId: string = "HANDSHAKE" | ||
@@ -147,2 +148,20 @@ private subscriptions: { | ||
this.connected = true | ||
const resolver = getActionResolver(this.handshakeActionResolverId) | ||
if (!resolver) { | ||
throw new VesselError("Handshake resolver not found") | ||
} | ||
this.log.info(`Resolving action ${this.handshakeActionResolverId}`) | ||
const { resolve, reject } = resolver | ||
removeActionResolver(this.handshakeActionResolverId) | ||
if (message.payload?.error) { | ||
reject(message.payload.error) | ||
return | ||
} | ||
resolve(message.payload) | ||
} | ||
@@ -266,6 +285,10 @@ | ||
connect() { | ||
this.log.info(`Connecting to child iframe ${this.childUrl}`) | ||
return new Promise<void>((resolve, reject) => { | ||
this.log.info(`Connecting to child iframe ${this.childUrl}`) | ||
this.frame.addEventListener("load", this.startHandshake.bind(this)) | ||
this.frame.src = this.childUrl | ||
this.frame.addEventListener("load", this.startHandshake.bind(this)) | ||
this.frame.src = this.childUrl | ||
addActionResolver(this.handshakeActionResolverId, { resolve, reject }) | ||
}) | ||
} | ||
@@ -272,0 +295,0 @@ |
76995
2029