@keyban/sdk-base
Version:
Keyban Javascript SDK provides core functionalities for the MPC wallet solution, supporting web and Node.js apps with TypeScript, custom storage, and Ethereum blockchain integration.
199 lines (197 loc) • 5.79 kB
JavaScript
import { IFrameApiError, KeybanBaseError, SdkError } from './chunk-7H2SLR6W.js';
// src/api.ts
var ApiServer = class _ApiServer {
constructor() {
window.addEventListener("message", this.#handleMessage);
}
// Forced validation of service's methods, see listener below
static #definitions = {
application: {
getCurrent: true
},
auth: {
isAuthenticated: true,
getUser: true,
sendOtp: true,
signUp: true,
signIn: true,
signOut: true,
updateUser: true
},
ecdsa: {
dkg: true,
sign: true,
publicKey: true
},
eddsa: {
dkg: true,
sign: true,
publicKey: true
},
account: {
getAddress: true,
getId: true
},
loyalty: {
getOptimisticBalance: true,
getRewardTier: true,
addToGoogleWallet: true,
addToAppleWallet: true
},
dpp: {
getProduct: true,
getPassport: true,
claim: true,
magicClaim: true
},
clientShareStorage: {
get: true,
set: true
}
};
#handleMessage = async (event) => {
if (!event.data.__KEYBAN_API) return;
try {
const originAllowed = await this.checkEventOrigin(event.origin);
if (!originAllowed)
throw new IFrameApiError(
IFrameApiError.types.InvalidOrigin,
"ApiServer"
);
const { serviceName, methodName } = event.data;
if (!_ApiServer.#definitions[serviceName]?.[methodName])
throw new IFrameApiError(
IFrameApiError.types.InvalidCall,
`ApiServer:${serviceName}.${methodName}`
);
const fn = this[serviceName]?.[methodName];
if (!fn)
throw new IFrameApiError(
IFrameApiError.types.InvalidCall,
`ApiServer:${serviceName}.${methodName}`
);
const result = await fn.apply(this[serviceName], event.data.args);
event.ports[0].postMessage([result, null]);
} catch (error) {
let err = error;
if (!(err instanceof KeybanBaseError)) {
console.error(error);
err = new SdkError(
SdkError.types.UnknownIframeApiError,
"ApiServer",
error
);
}
event.ports[0].postMessage([null, JSON.stringify(err)]);
}
};
static openPopup(url) {
const channel = new MessageChannel();
return new Promise((resolve) => {
channel.port1.onmessage = () => {
channel.port1.close();
resolve();
};
const message = {
type: "keyban:popup",
url: url.toString()
};
window.parent.postMessage(message, "*", [channel.port2]);
});
}
};
var ApiClient = class _ApiClient {
#iframeUrl;
#iframe;
static #instances = /* @__PURE__ */ new Map();
static #getIframeUrl({ apiUrl, appId, network }) {
const iframeUrl = new URL("/sdk-client/api", apiUrl);
iframeUrl.searchParams.set("appId", appId);
iframeUrl.searchParams.set("network", network);
return iframeUrl;
}
/**
* Returns a shared instance per unique iframe URL.
*
* @param options - API client options.
* @returns ApiClient instance bound to the provided options.
*/
static getInstance(options) {
const key = _ApiClient.#getIframeUrl(options).toString();
if (!_ApiClient.#instances.has(key))
_ApiClient.#instances.set(key, new _ApiClient(options));
return _ApiClient.#instances.get(key);
}
api;
constructor(options) {
this.#iframeUrl = _ApiClient.#getIframeUrl(options);
window.addEventListener("message", this.#popupHandler);
this.#iframe = new Promise((resolve, reject) => {
const iframe = Object.assign(document.createElement("iframe"), {
src: this.#iframeUrl,
hidden: true,
onload: () => resolve(iframe),
onerror: reject
});
document.body.appendChild(iframe);
});
this.api = new Proxy({}, {
// API Proxy
get: (_target, serviceName) => new Proxy({}, {
// Service Proxy
get: (_target2, methodName) => new Proxy(() => {
}, {
// Method Proxy
apply: (_target3, _thisArg, args) => this.#call(serviceName, methodName, args)
})
})
});
}
#popupHandler = async (event) => {
const iframe = await this.#iframe;
if (event.source !== iframe.contentWindow) return;
if (typeof event.data !== "object") return;
if (event.data.type != "keyban:popup") return;
const width = 500;
const height = 685;
const left = window.screenX + (window.innerWidth - width) / 2;
const top = window.screenY + (window.innerHeight - height) / 2;
const popup = window.open(
event.data.url,
`keyban:popup`,
`left=${left},top=${top},width=${width},height=${height},popup,resizable,scrollbars`
);
await new Promise((resolve) => {
const interval = setInterval(() => {
if (!popup?.closed) return;
clearInterval(interval);
resolve();
}, 100);
});
event.ports[0].postMessage("closed");
};
async #call(serviceName, methodName, args) {
const iframe = await this.#iframe;
const channel = new MessageChannel();
return await new Promise((resolve, reject) => {
channel.port1.onmessage = ({
data: [result, error]
}) => {
channel.port1.close();
return error != null ? reject(new KeybanBaseError(JSON.parse(error))) : resolve(result);
};
const message = {
__KEYBAN_API: true,
serviceName,
methodName,
args
};
iframe.contentWindow?.postMessage(message, this.#iframeUrl.origin, [
channel.port2
]);
});
}
};
export { ApiClient, ApiServer };
//# sourceMappingURL=chunk-5DFR5Z62.js.map
//# sourceMappingURL=chunk-5DFR5Z62.js.map