@shopify/app-bridge-core
Version:
**[Join our team and work on libraries like this one.](https://www.shopify.ca/careers)**
201 lines (200 loc) • 7.91 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", { value: true });
exports.Context = void 0;
exports.fromFrame = fromFrame;
exports.fromWindow = fromWindow;
exports.createTransportListener = createTransportListener;
var Error_1 = require("./actions/Error");
var validator_1 = require("./actions/validator");
var types_1 = require("./client/types");
var collection_1 = require("./util/collection");
var env_1 = require("./util/env");
var Context;
(function (Context) {
Context["Modal"] = "Modal";
Context["Main"] = "Main";
})(Context || (exports.Context = Context = {}));
/**
* Create a MessageTransport from a Frame.
* @remarks
* Used on the host-side to create a postMessage MessageTransport.
* @beta
*/
function fromFrame(frame, localOrigin, context) {
var handlers = [];
var host = frame.host, frameWindow = frame.window;
if (!host) {
throw (0, Error_1.fromAction)('App frame is undefined', Error_1.AppActionType.WINDOW_UNDEFINED);
}
if (env_1.isUnframed && window.MobileWebView) {
Object.assign(window.MobileWebView, {
postMessageToIframe: function (message, origin) {
frameWindow === null || frameWindow === void 0 ? void 0 : frameWindow.postMessage(message, origin);
if (isDispatchAction(message)) {
host.postMessage(JSON.stringify(message.payload), location.origin);
}
},
updateIframeUrl: function (newUrl) {
var currentWindowLocation = window.location;
var frameWindowLocation = (frame.window || {}).location;
try {
var newUrlOrigin = new URL(newUrl).origin;
if (newUrlOrigin === localOrigin && frameWindowLocation) {
frameWindowLocation.replace(newUrl);
}
else {
currentWindowLocation.href = newUrl;
}
}
catch (_) {
// Noop
}
},
});
}
host.addEventListener('message', function (event) {
if (event.source === host || !(0, validator_1.isAppMessage)(event)) {
return;
}
if (event.origin !== localOrigin) {
var errorMessage = "Message origin '".concat(event.origin, "' does not match app origin '").concat(localOrigin, "'.");
var payload = (0, Error_1.invalidOriginAction)(errorMessage);
var message = {
type: 'dispatch',
payload: payload,
};
frameWindow === null || frameWindow === void 0 ? void 0 : frameWindow.postMessage(message, event.origin);
return;
}
if (env_1.isUnframed && window.MobileWebView) {
var payload = JSON.stringify({
id: 'unframed://fromClient',
origin: localOrigin,
data: event.data,
});
window.MobileWebView.postMessage(payload);
return;
}
for (var _i = 0, handlers_1 = handlers; _i < handlers_1.length; _i++) {
var handler = handlers_1[_i];
handler(event);
}
});
return {
context: context,
localOrigin: localOrigin,
frameWindow: frameWindow,
hostFrame: host,
dispatch: function (message) {
frameWindow === null || frameWindow === void 0 ? void 0 : frameWindow.postMessage(message, localOrigin);
},
subscribe: function (handler) {
return (0, collection_1.addAndRemoveFromCollection)(handlers, handler);
},
};
}
/**
* Create a MessageTransport from a parent window.
* @remarks
* Used on the client-side to create a postMessage MessageTransport.
* @internalremarks
* In unframed mode, message should be dispatched via MobileWebView.postMessage instead of postMessage.
* @beta
*/
function fromWindow(contentWindow, localOrigin) {
var handlers = [];
if (typeof window !== undefined) {
window.addEventListener('message', function (event) {
if ((window === contentWindow && !env_1.isUnframed) ||
event.source !== contentWindow ||
!((0, validator_1.isAppBridgeAction)(event.data.payload) || (0, validator_1.isAppMessage)(event))) {
return;
}
for (var _i = 0, handlers_2 = handlers; _i < handlers_2.length; _i++) {
var handler = handlers_2[_i];
handler(event);
}
});
}
return {
localOrigin: localOrigin,
hostFrame: contentWindow,
dispatch: function (message) {
var _a;
if (!((_a = message.source) === null || _a === void 0 ? void 0 : _a.host)) {
return;
}
if (env_1.isUnframed && window && window.MobileWebView) {
var payload = JSON.stringify({
id: 'unframed://fromClient',
origin: localOrigin,
data: message,
});
window.MobileWebView.postMessage(payload);
return;
}
var messageOrigin = new URL("https://".concat(message.source.host)).origin;
contentWindow.postMessage(message, messageOrigin);
},
subscribe: function (handler) {
return (0, collection_1.addAndRemoveFromCollection)(handlers, handler);
},
};
}
function createTransportListener() {
var listeners = [];
var actionListeners = {};
function createSubscribeHandler(dispatcher) {
function subscribe() {
if (arguments.length < 2) {
// eslint-disable-next-line prefer-rest-params
return (0, collection_1.addAndRemoveFromCollection)(listeners, { callback: arguments[0] });
}
// eslint-disable-next-line prefer-rest-params
var _a = Array.from(arguments), type = _a[0], callback = _a[1], id = _a[2];
var actionCallback = { callback: callback, id: id };
var payload = { type: type, id: id };
if (!Object.prototype.hasOwnProperty.call(actionListeners, type)) {
actionListeners[type] = [];
}
if (dispatcher) {
dispatcher(types_1.MessageType.Subscribe, payload);
}
return (0, collection_1.addAndRemoveFromCollection)(actionListeners[type], actionCallback, function () {
if (dispatcher) {
dispatcher(types_1.MessageType.Unsubscribe, payload);
}
});
}
return subscribe;
}
return {
createSubscribeHandler: createSubscribeHandler,
handleMessage: function (message) {
listeners.forEach(function (listener) { return listener.callback(message); });
},
handleActionDispatch: function (_a) {
var type = _a.type, payload = _a.payload;
var hasCallback = false;
if (Object.prototype.hasOwnProperty.call(actionListeners, type)) {
for (var _i = 0, _b = actionListeners[type]; _i < _b.length; _i++) {
var listener = _b[_i];
var id = listener.id, callback = listener.callback;
var matchId = payload && payload.id === id;
if (matchId || !id) {
callback(payload);
hasCallback = true;
}
}
}
return hasCallback;
},
};
}
function isDispatchAction(message) {
return (message !== null &&
typeof message === 'object' &&
!Array.isArray(message) &&
message.type === 'dispatch' &&
typeof message.payload === 'object');
}