@shopify/app-bridge-host
Version:
App Bridge Host contains middleware and components that are meant to be consumed by the app's host. The middleware and `Frame` component are responsible for facilitating messages posted between the client and host, and used to act on actions sent from the
173 lines (172 loc) • 7.88 kB
JavaScript
;
var __assign = (this && this.__assign) || function () {
__assign = Object.assign || function(t) {
for (var s, i = 1, n = arguments.length; i < n; i++) {
s = arguments[i];
for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p))
t[p] = s[p];
}
return t;
};
return __assign.apply(this, arguments);
};
Object.defineProperty(exports, "__esModule", { value: true });
var app_bridge_1 = require("@shopify/app-bridge");
var validator_1 = require("@shopify/app-bridge/actions/validator");
var Error_1 = require("@shopify/app-bridge/actions/Error");
var collection_1 = require("@shopify/app-bridge/util/collection");
var actions_1 = require("./actions");
var clientValidator_1 = require("./clientValidator");
function buildMiddleware(key, dispatchClientEventHandler) {
var transports = [];
var subscribers = [];
var store;
var middleware = function (hostStore) {
store = hostStore;
return function (next) { return function (action) {
if (validator_1.isAppBridgeAction(action)) {
for (var _i = 0, transports_1 = transports; _i < transports_1.length; _i++) {
var transport = transports_1[_i];
var features = store.getState()[key].features[transport.context];
if (validator_1.isPermitted(features, action, app_bridge_1.PermissionType.Subscribe)) {
transport.dispatch({
payload: action,
type: 'dispatch',
});
}
}
}
return next(action);
}; };
};
middleware.load = function provideApplicationInterface(data) {
var config = data.config;
var clientHandlers = createClientHandlers();
store.dispatch(actions_1.apiClientLoad(config));
return {
attach: function (to) {
var _this = this;
var contextualClientHandlers = clientHandlers[to.context];
contextualClientHandlers.unsubscribe();
var unsubscribe = to.subscribe(function (event) {
var context = to.context;
var action = event.data;
var type = action && action.type;
var payload = action && action.payload;
var source = action && action.source;
if (!clientValidator_1.isValidConfig(source, config)) {
clientValidator_1.throwInvalidConfigError(source, config, payload);
}
if (to.frameWindow !== event.source) {
return;
}
for (var _i = 0, subscribers_1 = subscribers; _i < subscribers_1.length; _i++) {
var listener = subscribers_1[_i];
listener(action);
}
switch (type) {
case 'dispatch':
var features = _this.getState().features[context];
if (!validator_1.isPermitted(features, payload, app_bridge_1.PermissionType.Dispatch)) {
store.dispatch(Error_1.permissionAction(payload));
return;
}
store.dispatch(__assign({}, payload, { source: source }));
if (dispatchClientEventHandler) {
var appId = config.appId, shopId = config.shopId;
dispatchClientEventHandler({
action: payload,
appId: appId,
shopId: shopId,
});
}
break;
case 'getState':
var defaultState = _this.getState();
var state = __assign({}, defaultState, { features: defaultState.features[context], context: context });
to.dispatch({
type: type,
payload: state,
});
break;
case 'subscribe':
contextualClientHandlers.subscribe(payload);
break;
case 'unsubscribe':
contextualClientHandlers.unsubscribe(payload);
break;
default:
Error_1.throwError(Error_1.ActionType.INVALID_ACTION, action, 'Unknown action type. Expected `dispatch` or `getState`.');
}
});
var detach = collection_1.addAndRemoveFromCollection(transports, to, unsubscribe);
return function (unload) {
if (unload === void 0) { unload = true; }
var origin = new URL(config.url).origin;
var removed = detach() && !transports.find(function (transport) { return transport.localOrigin === origin; });
contextualClientHandlers.unsubscribe();
if (removed && unload) {
store.dispatch(actions_1.apiClientUnload(config));
}
};
},
dispatch: function (action) {
store.dispatch(action);
},
getState: function () {
return store.getState()[key];
},
subscribe: function (listener) {
return collection_1.addAndRemoveFromCollection(subscribers, listener);
},
isTransportSubscribed: function (context, type, id) {
return clientHandlers[context].isSubscribed(type, id);
},
};
};
return middleware;
}
exports.buildMiddleware = buildMiddleware;
function createClientHandlers() {
var _a, _b;
var subscriptions = (_a = {}, _a[app_bridge_1.Context.Main] = {}, _a[app_bridge_1.Context.Modal] = {}, _a);
return _b = {},
_b[app_bridge_1.Context.Main] = createSubscriptionsHandler(subscriptions[app_bridge_1.Context.Main]),
_b[app_bridge_1.Context.Modal] = createSubscriptionsHandler(subscriptions[app_bridge_1.Context.Modal]),
_b;
}
function createSubscriptionsHandler(subscriptions) {
return {
isSubscribed: function (type, id) {
var contextSubscribers = subscriptions[type] || [];
var subscribers = contextSubscribers.filter(function (sub) { return sub.id === id; });
return subscribers.length > 0;
},
subscribe: function (payload) {
var type = payload.type;
if (!subscriptions[type]) {
subscriptions[type] = [];
}
var eventSubscriptions = subscriptions[type] || [];
collection_1.addAndRemoveFromCollection(eventSubscriptions, payload);
},
unsubscribe: function (payload) {
if (!payload) {
subscriptions = {};
return;
}
var type = payload.type, id = payload.id;
var eventSubscriptions = subscriptions[type];
if (!eventSubscriptions) {
return;
}
if (id) {
var index = eventSubscriptions.findIndex(function (sub) { return sub.id === id; });
if (index >= 0) {
return eventSubscriptions.splice(index);
}
}
eventSubscriptions.pop();
},
};
}