UNPKG

10.6 kBJavaScriptView Raw
1"use strict";
2var __assign = (this && this.__assign) || function () {
3 __assign = Object.assign || function(t) {
4 for (var s, i = 1, n = arguments.length; i < n; i++) {
5 s = arguments[i];
6 for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p))
7 t[p] = s[p];
8 }
9 return t;
10 };
11 return __assign.apply(this, arguments);
12};
13var __importDefault = (this && this.__importDefault) || function (mod) {
14 return (mod && mod.__esModule) ? mod : { "default": mod };
15};
16Object.defineProperty(exports, "__esModule", { value: true });
17var helper_1 = require("../actions/helper");
18var Error_1 = require("../actions/Error");
19var Print_1 = require("../actions/Print");
20var MessageTransport_1 = require("../MessageTransport");
21var collection_1 = require("../util/collection");
22var shared_1 = require("../util/shared");
23var env_1 = require("../util/env");
24var print_1 = require("./print");
25var redirect_1 = require("./redirect");
26var types_1 = require("./types");
27var Hooks_1 = __importDefault(require("./Hooks"));
28exports.WINDOW_UNDEFINED_MESSAGE = 'window is not defined. Running an app outside a browser is not supported';
29function redirectHandler(hostFrame, config) {
30 var apiKey = config.apiKey, shopOrigin = config.shopOrigin, _a = config.forceRedirect, forceRedirect = _a === void 0 ? !env_1.isDevelopmentClient : _a;
31 var location = redirect_1.getLocation();
32 if (env_1.isFrameless ||
33 !location ||
34 !apiKey ||
35 !shopOrigin ||
36 !forceRedirect ||
37 !redirect_1.shouldRedirect(hostFrame)) {
38 return;
39 }
40 var url = "https://" + shopOrigin + "/admin/apps/" + apiKey + location.pathname + (location.search ||
41 '');
42 redirect_1.redirect(url);
43}
44function appSetUp(app) {
45 app.subscribe(Print_1.ActionType.APP, print_1.handleAppPrint);
46}
47/**
48 * Extracts the query parameters from the current URL.
49 * @deprecated This function has been deprecated.
50 * @public
51 */
52function getUrlParams() {
53 var params = {};
54 var location = redirect_1.getLocation();
55 if (!location) {
56 return params;
57 }
58 var hashes = location.search.slice(location.search.indexOf('?') + 1).split('&');
59 return hashes.reduce(function (acc, hash) {
60 var _a;
61 var _b = hash.split('='), key = _b[0], val = _b[1];
62 return __assign({}, acc, (_a = {}, _a[decodeURIComponent(key)] = decodeURIComponent(val), _a));
63 }, params);
64}
65exports.getUrlParams = getUrlParams;
66/**
67 * Extracts the `shop` query parameter from the current URL.
68 * @deprecated This function has been deprecated, see {@link https://help.shopify.com/api/embedded-apps/shop-origin}
69 * @public
70 */
71function getShopOrigin() {
72 var params = getUrlParams();
73 return params.shop;
74}
75exports.getShopOrigin = getShopOrigin;
76/**
77 * @internal
78 */
79exports.createClientApp = function (transport, middlewares) {
80 if (middlewares === void 0) { middlewares = []; }
81 var getStateListeners = [];
82 var listeners = [];
83 var actionListeners = {};
84 var invokeCallbacks = function (type, payload) {
85 var hasCallback = false;
86 if (actionListeners.hasOwnProperty(type)) {
87 for (var _i = 0, _a = actionListeners[type]; _i < _a.length; _i++) {
88 var listener = _a[_i];
89 var id = listener.id, callback = listener.callback;
90 var matchId = payload && payload.id === id;
91 if (matchId || !id) {
92 callback(payload);
93 hasCallback = true;
94 }
95 }
96 }
97 if (hasCallback) {
98 return;
99 }
100 // Throw an error if there are no subscriptions to this error
101 var errorType = helper_1.findMatchInEnum(Error_1.ActionType, type);
102 if (errorType) {
103 Error_1.throwError(errorType, payload);
104 }
105 };
106 var handler = function (event) {
107 var action = event.data;
108 switch (action.type) {
109 case 'getState':
110 var resolvers = getStateListeners.splice(0);
111 resolvers.forEach(function (resolver) { return resolver(action.payload); });
112 break;
113 case 'dispatch':
114 var payload_1 = action.payload;
115 invokeCallbacks(payload_1.type, payload_1.payload);
116 listeners.forEach(function (listener) { return listener.callback(payload_1); });
117 break;
118 default:
119 // Silently swallow unknown actions
120 }
121 };
122 transport.subscribe(handler);
123 return function (config) {
124 if (!config.shopOrigin) {
125 throw Error_1.fromAction('shopOrigin must be provided', Error_1.AppActionType.INVALID_CONFIG);
126 }
127 var protocol = /^https?:\/\//;
128 if (protocol.test(config.shopOrigin)) {
129 var message = "shopOrigin should not include protocol, please use: " + config.shopOrigin.replace(protocol, '');
130 throw Error_1.fromAction(message, Error_1.AppActionType.INVALID_CONFIG);
131 }
132 if (!config.apiKey) {
133 throw Error_1.fromAction('apiKey must be provided', Error_1.AppActionType.INVALID_CONFIG);
134 }
135 var dispatcher = createDispatcher(transport, config);
136 // It is possible to initialize an app multiple times
137 // Therefore we need to clear subscriptions to be safe
138 dispatcher(types_1.MessageType.Unsubscribe);
139 function dispatch(action) {
140 dispatcher(types_1.MessageType.Dispatch, action);
141 return action;
142 }
143 redirectHandler(transport.hostFrame, config);
144 var hooks = new Hooks_1.default();
145 var app = {
146 localOrigin: transport.localOrigin,
147 hooks: hooks,
148 dispatch: function (action) {
149 if (!app.hooks) {
150 return dispatch(action);
151 }
152 return app.hooks.run(types_1.LifecycleHook.DispatchAction, dispatch, app, action);
153 },
154 featuresAvailable: function (features) {
155 return app.getState('features').then(function (state) {
156 if (features) {
157 Object.keys(state).forEach(function (feature) {
158 if (!features.includes(feature)) {
159 delete state[feature];
160 }
161 });
162 }
163 return state;
164 });
165 },
166 getState: function (query) {
167 return new Promise(function (resolve) {
168 getStateListeners.push(resolve);
169 dispatcher(types_1.MessageType.GetState);
170 }).then(function (state) {
171 if (query) {
172 return query.split('.').reduce(function (value, key) {
173 if (typeof state !== 'object' || Array.isArray(state)) {
174 return undefined;
175 }
176 value = state[key];
177 state = value;
178 return value;
179 }, undefined);
180 }
181 return state;
182 });
183 },
184 subscribe: function () {
185 if (arguments.length < 2) {
186 return collection_1.addAndRemoveFromCollection(listeners, { callback: arguments[0] });
187 }
188 var type = arguments[0];
189 var callback = arguments[1];
190 var id = arguments[2];
191 var actionCallback = { callback: callback, id: id };
192 var payload = { type: type, id: id };
193 if (!actionListeners.hasOwnProperty(type)) {
194 actionListeners[type] = [];
195 }
196 dispatcher(types_1.MessageType.Subscribe, payload);
197 return collection_1.addAndRemoveFromCollection(actionListeners[type], actionCallback, function () {
198 return dispatcher(types_1.MessageType.Unsubscribe, payload);
199 });
200 },
201 error: function (listener, id) {
202 var _this = this;
203 var unsubscribeCb = [];
204 helper_1.forEachInEnum(Error_1.ActionType, function (eventNameSpace) {
205 // tslint:disable-next-line:no-invalid-this
206 unsubscribeCb.push(_this.subscribe(eventNameSpace, listener, id));
207 });
208 return function () {
209 unsubscribeCb.forEach(function (unsubscribe) { return unsubscribe(); });
210 };
211 },
212 };
213 for (var _i = 0, middlewares_1 = middlewares; _i < middlewares_1.length; _i++) {
214 var middleware = middlewares_1[_i];
215 middleware(hooks, app);
216 }
217 appSetUp(app);
218 return app;
219 };
220};
221/**
222 * @public
223 */
224function createAppWrapper(frame, localOrigin, middleware) {
225 if (middleware === void 0) { middleware = []; }
226 if (!frame) {
227 throw Error_1.fromAction(exports.WINDOW_UNDEFINED_MESSAGE, Error_1.AppActionType.WINDOW_UNDEFINED);
228 }
229 var location = redirect_1.getLocation();
230 var origin = localOrigin || (location && location.origin);
231 if (!origin) {
232 throw Error_1.fromAction('local origin cannot be blank', Error_1.AppActionType.MISSING_LOCAL_ORIGIN);
233 }
234 var transport = MessageTransport_1.fromWindow(frame, origin);
235 var appCreator = exports.createClientApp(transport, middleware);
236 return appCreator;
237}
238exports.createAppWrapper = createAppWrapper;
239/**
240 * Creates your application instance.
241 * @param config - Both `apiKey` and `shopOrigin` are required.
242 * @remarks
243 * You will need to store `shopOrigin` during the authentication process and then retrieve it for the code to work properly. To learn more about this process, see {@link https://help.shopify.com/api/embedded-apps/shop-origin | Getting and storing the shop origin}.
244 * @public
245 */
246function createApp(config) {
247 var currentWindow = redirect_1.getWindow();
248 if (!currentWindow) {
249 return shared_1.serverAppBridge;
250 }
251 return createAppWrapper(currentWindow.top)(config);
252}
253exports.createApp = createApp;
254function createDispatcher(transport, config) {
255 return function (type, payload) {
256 transport.dispatch({
257 payload: payload,
258 source: config,
259 type: type,
260 });
261 };
262}
263/**
264 * {@inheritdocs createApp}
265 * @public
266 */
267exports.default = createApp;