@agnostack/next-shopify 3.3.1-alpha.1 → 4.0.0-canary.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (235) hide show
  1. package/dist/esm/lib/handlers/api/auth.js +69 -0
  2. package/dist/esm/lib/handlers/api/billing.js +98 -0
  3. package/dist/esm/lib/handlers/api/callback.js +162 -0
  4. package/dist/esm/lib/handlers/api/index.js +17 -0
  5. package/dist/esm/lib/handlers/api/types.js +0 -0
  6. package/dist/esm/lib/handlers/api/uninstall.js +75 -0
  7. package/dist/esm/lib/handlers/api/verifiable/appInstallation.js +37 -0
  8. package/dist/esm/lib/handlers/api/verifiable/graph.js +149 -0
  9. package/dist/esm/lib/handlers/api/verifiable/index.js +4 -0
  10. package/dist/esm/lib/handlers/api/verifiable/proxy.js +202 -0
  11. package/dist/esm/lib/handlers/api/verifiable/token.js +102 -0
  12. package/dist/esm/lib/handlers/api/webhook.js +114 -0
  13. package/dist/esm/lib/handlers/index.js +2 -0
  14. package/dist/esm/lib/handlers/ssr/index.js +2 -0
  15. package/dist/esm/lib/handlers/ssr/protect.js +143 -0
  16. package/dist/esm/lib/handlers/ssr/types.js +0 -0
  17. package/dist/esm/lib/handlers/ssr/wrap.js +20 -0
  18. package/dist/esm/lib/handlers/types.js +0 -0
  19. package/dist/esm/lib/index.js +3 -0
  20. package/dist/esm/lib/types.js +0 -0
  21. package/dist/esm/lib/utils/billing.js +65 -0
  22. package/dist/esm/lib/utils/crypto.js +159 -0
  23. package/dist/esm/lib/utils/firebase.js +263 -0
  24. package/dist/esm/lib/utils/headers.js +21 -0
  25. package/dist/esm/lib/utils/index.js +9 -0
  26. package/dist/esm/lib/utils/json.js +74 -0
  27. package/dist/esm/lib/utils/session.js +431 -0
  28. package/dist/esm/lib/utils/shopify-api-js/oauth.js +56 -0
  29. package/dist/esm/lib/utils/shopify.js +128 -0
  30. package/dist/esm/lib/utils/token.js +142 -0
  31. package/dist/esm/lib/utils/types.js +1 -0
  32. package/dist/esm/package.json +3 -0
  33. package/dist/esm/plugins/index.js +2 -0
  34. package/dist/esm/plugins/types.js +0 -0
  35. package/dist/esm/plugins/withShopify.js +251 -0
  36. package/dist/esm/runtime/index.js +1 -0
  37. package/dist/esm/runtime/types.js +0 -0
  38. package/dist/esm/shared/constants.js +258 -0
  39. package/dist/esm/shared/datetime.js +387 -0
  40. package/dist/esm/shared/display.js +638 -0
  41. package/dist/esm/shared/errors.js +53 -0
  42. package/dist/esm/shared/graph.js +218 -0
  43. package/dist/esm/shared/index.js +9 -0
  44. package/dist/esm/shared/match.js +61 -0
  45. package/dist/esm/shared/shopify.js +98 -0
  46. package/dist/esm/shared/theme.js +21 -0
  47. package/dist/esm/shared/types.js +0 -0
  48. package/dist/esm/shared/uuid.js +4 -0
  49. package/dist/lib/handlers/api/auth.d.ts +1 -2
  50. package/dist/lib/handlers/api/auth.js +28 -22
  51. package/dist/lib/handlers/api/billing.d.ts +1 -2
  52. package/dist/lib/handlers/api/billing.js +34 -25
  53. package/dist/lib/handlers/api/callback.d.ts +1 -2
  54. package/dist/lib/handlers/api/callback.js +51 -44
  55. package/dist/lib/handlers/api/index.d.ts +11 -10
  56. package/dist/lib/handlers/api/index.js +17 -11
  57. package/dist/lib/handlers/api/types.d.ts +1 -1
  58. package/dist/lib/handlers/api/types.js +2 -1
  59. package/dist/lib/handlers/api/uninstall.d.ts +1 -2
  60. package/dist/lib/handlers/api/uninstall.js +26 -21
  61. package/dist/lib/handlers/api/verifiable/appInstallation.d.ts +1 -2
  62. package/dist/lib/handlers/api/verifiable/appInstallation.js +21 -18
  63. package/dist/lib/handlers/api/verifiable/graph.d.ts +1 -2
  64. package/dist/lib/handlers/api/verifiable/graph.js +44 -34
  65. package/dist/lib/handlers/api/verifiable/index.d.ts +4 -5
  66. package/dist/lib/handlers/api/verifiable/index.js +4 -5
  67. package/dist/lib/handlers/api/verifiable/proxy.d.ts +1 -2
  68. package/dist/lib/handlers/api/verifiable/proxy.js +63 -53
  69. package/dist/lib/handlers/api/verifiable/token.d.ts +1 -2
  70. package/dist/lib/handlers/api/verifiable/token.js +34 -33
  71. package/dist/lib/handlers/api/webhook.d.ts +1 -2
  72. package/dist/lib/handlers/api/webhook.js +40 -32
  73. package/dist/lib/handlers/index.d.ts +2 -3
  74. package/dist/lib/handlers/index.js +2 -3
  75. package/dist/lib/handlers/ssr/index.d.ts +2 -3
  76. package/dist/lib/handlers/ssr/index.js +2 -3
  77. package/dist/lib/handlers/ssr/protect.d.ts +2 -9
  78. package/dist/lib/handlers/ssr/protect.js +50 -45
  79. package/dist/lib/handlers/ssr/types.d.ts +1 -1
  80. package/dist/lib/handlers/ssr/types.js +2 -1
  81. package/dist/lib/handlers/ssr/wrap.d.ts +1 -7
  82. package/dist/lib/handlers/ssr/wrap.js +13 -18
  83. package/dist/lib/handlers/types.d.ts +1 -1
  84. package/dist/lib/handlers/types.js +2 -1
  85. package/dist/lib/index.d.ts +3 -4
  86. package/dist/lib/index.js +5 -6
  87. package/dist/lib/types.d.ts +1 -1
  88. package/dist/lib/types.js +2 -1
  89. package/dist/lib/utils/billing.d.ts +2 -15
  90. package/dist/lib/utils/billing.js +25 -25
  91. package/dist/lib/utils/crypto.d.ts +21 -8
  92. package/dist/lib/utils/crypto.js +62 -6
  93. package/dist/lib/utils/firebase.d.ts +23 -20
  94. package/dist/lib/utils/firebase.js +93 -52
  95. package/dist/lib/utils/headers.d.ts +1 -5
  96. package/dist/lib/utils/headers.js +11 -4
  97. package/dist/lib/utils/index.d.ts +9 -9
  98. package/dist/lib/utils/index.js +9 -9
  99. package/dist/lib/utils/json.d.ts +1 -0
  100. package/dist/lib/utils/json.js +81 -0
  101. package/dist/lib/utils/session.d.ts +3 -64
  102. package/dist/lib/utils/session.js +201 -148
  103. package/dist/lib/utils/shopify-api-js/oauth.d.ts +2 -8
  104. package/dist/lib/utils/shopify-api-js/oauth.js +14 -27
  105. package/dist/lib/utils/shopify.d.ts +6 -17
  106. package/dist/lib/utils/shopify.js +45 -32
  107. package/dist/lib/utils/token.d.ts +3 -8
  108. package/dist/lib/utils/token.js +112 -25
  109. package/dist/lib/utils/types.d.ts +58 -1
  110. package/dist/lib/utils/types.js +2 -1
  111. package/dist/plugins/index.d.ts +2 -3
  112. package/dist/plugins/index.js +2 -3
  113. package/dist/plugins/types.d.ts +1 -1
  114. package/dist/plugins/types.js +2 -1
  115. package/dist/plugins/withShopify.d.ts +1 -6
  116. package/dist/plugins/withShopify.js +81 -87
  117. package/dist/runtime/index.d.ts +1 -2
  118. package/dist/runtime/index.js +1 -2
  119. package/dist/runtime/types.d.ts +1 -1
  120. package/dist/runtime/types.js +2 -1
  121. package/dist/shared/constants.d.ts +4 -3
  122. package/dist/shared/constants.js +3 -2
  123. package/dist/shared/datetime.d.ts +122 -69
  124. package/dist/shared/datetime.js +118 -25
  125. package/dist/shared/display.d.ts +92 -91
  126. package/dist/shared/display.js +214 -3
  127. package/dist/shared/errors.d.ts +5 -2
  128. package/dist/shared/errors.js +16 -1
  129. package/dist/shared/graph.d.ts +9 -4
  130. package/dist/shared/graph.js +15 -10
  131. package/dist/shared/index.d.ts +9 -10
  132. package/dist/shared/index.js +9 -10
  133. package/dist/shared/match.d.ts +5 -6
  134. package/dist/shared/match.js +23 -7
  135. package/dist/shared/shopify.d.ts +9 -15
  136. package/dist/shared/shopify.js +29 -19
  137. package/dist/shared/theme.d.ts +2 -3
  138. package/dist/shared/theme.js +6 -3
  139. package/dist/shared/types.d.ts +1 -1
  140. package/dist/shared/types.js +2 -1
  141. package/dist/shared/uuid.d.ts +2 -3
  142. package/dist/shared/uuid.js +0 -1
  143. package/package.json +104 -61
  144. package/dist/lib/handlers/api/auth.d.ts.map +0 -1
  145. package/dist/lib/handlers/api/auth.js.map +0 -1
  146. package/dist/lib/handlers/api/billing.d.ts.map +0 -1
  147. package/dist/lib/handlers/api/billing.js.map +0 -1
  148. package/dist/lib/handlers/api/callback.d.ts.map +0 -1
  149. package/dist/lib/handlers/api/callback.js.map +0 -1
  150. package/dist/lib/handlers/api/index.d.ts.map +0 -1
  151. package/dist/lib/handlers/api/index.js.map +0 -1
  152. package/dist/lib/handlers/api/types.d.ts.map +0 -1
  153. package/dist/lib/handlers/api/types.js.map +0 -1
  154. package/dist/lib/handlers/api/uninstall.d.ts.map +0 -1
  155. package/dist/lib/handlers/api/uninstall.js.map +0 -1
  156. package/dist/lib/handlers/api/verifiable/appInstallation.d.ts.map +0 -1
  157. package/dist/lib/handlers/api/verifiable/appInstallation.js.map +0 -1
  158. package/dist/lib/handlers/api/verifiable/graph.d.ts.map +0 -1
  159. package/dist/lib/handlers/api/verifiable/graph.js.map +0 -1
  160. package/dist/lib/handlers/api/verifiable/index.d.ts.map +0 -1
  161. package/dist/lib/handlers/api/verifiable/index.js.map +0 -1
  162. package/dist/lib/handlers/api/verifiable/proxy.d.ts.map +0 -1
  163. package/dist/lib/handlers/api/verifiable/proxy.js.map +0 -1
  164. package/dist/lib/handlers/api/verifiable/token.d.ts.map +0 -1
  165. package/dist/lib/handlers/api/verifiable/token.js.map +0 -1
  166. package/dist/lib/handlers/api/webhook.d.ts.map +0 -1
  167. package/dist/lib/handlers/api/webhook.js.map +0 -1
  168. package/dist/lib/handlers/index.d.ts.map +0 -1
  169. package/dist/lib/handlers/index.js.map +0 -1
  170. package/dist/lib/handlers/ssr/index.d.ts.map +0 -1
  171. package/dist/lib/handlers/ssr/index.js.map +0 -1
  172. package/dist/lib/handlers/ssr/protect.d.ts.map +0 -1
  173. package/dist/lib/handlers/ssr/protect.js.map +0 -1
  174. package/dist/lib/handlers/ssr/types.d.ts.map +0 -1
  175. package/dist/lib/handlers/ssr/types.js.map +0 -1
  176. package/dist/lib/handlers/ssr/wrap.d.ts.map +0 -1
  177. package/dist/lib/handlers/ssr/wrap.js.map +0 -1
  178. package/dist/lib/handlers/types.d.ts.map +0 -1
  179. package/dist/lib/handlers/types.js.map +0 -1
  180. package/dist/lib/index.d.ts.map +0 -1
  181. package/dist/lib/index.js.map +0 -1
  182. package/dist/lib/types.d.ts.map +0 -1
  183. package/dist/lib/types.js.map +0 -1
  184. package/dist/lib/utils/billing.d.ts.map +0 -1
  185. package/dist/lib/utils/billing.js.map +0 -1
  186. package/dist/lib/utils/crypto.d.ts.map +0 -1
  187. package/dist/lib/utils/crypto.js.map +0 -1
  188. package/dist/lib/utils/firebase.d.ts.map +0 -1
  189. package/dist/lib/utils/firebase.js.map +0 -1
  190. package/dist/lib/utils/headers.d.ts.map +0 -1
  191. package/dist/lib/utils/headers.js.map +0 -1
  192. package/dist/lib/utils/index.d.ts.map +0 -1
  193. package/dist/lib/utils/index.js.map +0 -1
  194. package/dist/lib/utils/session.d.ts.map +0 -1
  195. package/dist/lib/utils/session.js.map +0 -1
  196. package/dist/lib/utils/shopify-api-js/oauth.d.ts.map +0 -1
  197. package/dist/lib/utils/shopify-api-js/oauth.js.map +0 -1
  198. package/dist/lib/utils/shopify.d.ts.map +0 -1
  199. package/dist/lib/utils/shopify.js.map +0 -1
  200. package/dist/lib/utils/token.d.ts.map +0 -1
  201. package/dist/lib/utils/token.js.map +0 -1
  202. package/dist/lib/utils/types.d.ts.map +0 -1
  203. package/dist/lib/utils/types.js.map +0 -1
  204. package/dist/plugins/index.d.ts.map +0 -1
  205. package/dist/plugins/index.js.map +0 -1
  206. package/dist/plugins/types.d.ts.map +0 -1
  207. package/dist/plugins/types.js.map +0 -1
  208. package/dist/plugins/withShopify.d.ts.map +0 -1
  209. package/dist/plugins/withShopify.js.map +0 -1
  210. package/dist/runtime/index.d.ts.map +0 -1
  211. package/dist/runtime/index.js.map +0 -1
  212. package/dist/runtime/types.d.ts.map +0 -1
  213. package/dist/runtime/types.js.map +0 -1
  214. package/dist/shared/constants.d.ts.map +0 -1
  215. package/dist/shared/constants.js.map +0 -1
  216. package/dist/shared/datetime.d.ts.map +0 -1
  217. package/dist/shared/datetime.js.map +0 -1
  218. package/dist/shared/display.d.ts.map +0 -1
  219. package/dist/shared/display.js.map +0 -1
  220. package/dist/shared/errors.d.ts.map +0 -1
  221. package/dist/shared/errors.js.map +0 -1
  222. package/dist/shared/graph.d.ts.map +0 -1
  223. package/dist/shared/graph.js.map +0 -1
  224. package/dist/shared/index.d.ts.map +0 -1
  225. package/dist/shared/index.js.map +0 -1
  226. package/dist/shared/match.d.ts.map +0 -1
  227. package/dist/shared/match.js.map +0 -1
  228. package/dist/shared/shopify.d.ts.map +0 -1
  229. package/dist/shared/shopify.js.map +0 -1
  230. package/dist/shared/theme.d.ts.map +0 -1
  231. package/dist/shared/theme.js.map +0 -1
  232. package/dist/shared/types.d.ts.map +0 -1
  233. package/dist/shared/types.js.map +0 -1
  234. package/dist/shared/uuid.d.ts.map +0 -1
  235. package/dist/shared/uuid.js.map +0 -1
@@ -0,0 +1,69 @@
1
+ var __rest = (this && this.__rest) || function (s, e) {
2
+ var t = {};
3
+ for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p) && e.indexOf(p) < 0)
4
+ t[p] = s[p];
5
+ if (s != null && typeof Object.getOwnPropertySymbols === "function")
6
+ for (var i = 0, p = Object.getOwnPropertySymbols(s); i < p.length; i++) {
7
+ if (e.indexOf(p[i]) < 0 && Object.prototype.propertyIsEnumerable.call(s, p[i]))
8
+ t[p[i]] = s[p[i]];
9
+ }
10
+ return t;
11
+ };
12
+ import { beginAuth, getCallback, getInitializedShopify, } from '../../utils/index.js';
13
+ import { isTrue, stringEmpty, PAGE_ROUTE_NAMES, } from '../../../shared/index.js';
14
+ // NOTE: typically handling: "/api/auth/online" and "/api/auth/offline"
15
+ /**
16
+ * @param {any} [serverRuntimeConfig]
17
+ * @param {any} [data]
18
+ */
19
+ export const handleAuth = (serverRuntimeConfig, data) => {
20
+ const { PAGE_PATHS, API_PATHS, API_URLS, APP_CONFIG, } = serverRuntimeConfig;
21
+ const { returnResponse = true } = data !== null && data !== void 0 ? data : {};
22
+ /**
23
+ * @param {any} req
24
+ * @param {any} res
25
+ * @param {any} params
26
+ */
27
+ return async (req, res, params) => {
28
+ var _a;
29
+ const { isOnline } = params !== null && params !== void 0 ? params : {};
30
+ const _b = (_a = req.query) !== null && _a !== void 0 ? _a : {}, { reAuth: _reAuth } = _b, query = __rest(_b, ["reAuth"]);
31
+ const { embedded, state, host, shop: _shop } = query;
32
+ const reAuth = isTrue(_reAuth, 'undefined');
33
+ const shopify = getInitializedShopify(serverRuntimeConfig);
34
+ if (!shopify) {
35
+ throw new Error('Shopify API is not initialized');
36
+ }
37
+ const shop = shopify.utils.sanitizeShop(_shop);
38
+ /**
39
+ * @param {any} [body]
40
+ * @param {any} [statusCode]
41
+ */
42
+ const handleResponse = (body, statusCode = 307) => {
43
+ if (returnResponse) {
44
+ return res.redirect(statusCode, body);
45
+ }
46
+ return { statusCode, body };
47
+ };
48
+ if (stringEmpty(shop)) {
49
+ return handleResponse(`${PAGE_PATHS[PAGE_ROUTE_NAMES.ERROR]}?code=no_shop_provided&type=handleAuth`);
50
+ }
51
+ if (embedded === '1') {
52
+ const callbackUrl = getCallback(API_URLS, { reAuth, isOnline });
53
+ // TODO: explore objectToQuerystring
54
+ const redirectUriParams = new URLSearchParams(Object.assign({ shop,
55
+ host }, state && { state })).toString();
56
+ const queryParams = new URLSearchParams(Object.assign(Object.assign({}, query), { shop, redirectUri: `${callbackUrl}?${redirectUriParams}` })).toString();
57
+ return handleResponse(`${PAGE_PATHS[PAGE_ROUTE_NAMES.EXIT]}?${queryParams}`);
58
+ }
59
+ return beginAuth({
60
+ shop,
61
+ state,
62
+ isOnline,
63
+ callbackPath: getCallback(API_PATHS, { reAuth, isOnline }),
64
+ rawRequest: req,
65
+ rawResponse: res,
66
+ config: APP_CONFIG,
67
+ });
68
+ };
69
+ };
@@ -0,0 +1,98 @@
1
+ var __rest = (this && this.__rest) || function (s, e) {
2
+ var t = {};
3
+ for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p) && e.indexOf(p) < 0)
4
+ t[p] = s[p];
5
+ if (s != null && typeof Object.getOwnPropertySymbols === "function")
6
+ for (var i = 0, p = Object.getOwnPropertySymbols(s); i < p.length; i++) {
7
+ if (e.indexOf(p[i]) < 0 && Object.prototype.propertyIsEnumerable.call(s, p[i]))
8
+ t[p[i]] = s[p[i]];
9
+ }
10
+ return t;
11
+ };
12
+ import { ensureRawBody } from '@agnostack/verifyd';
13
+ import { getShopifyHelpers } from '../../utils/index.js';
14
+ import { ensureArray, stringEmpty, safeParse, APP_QUERY, BILLING_EVENT_TYPES, } from '../../../shared/index.js';
15
+ const TERMINAL_STATUSES = [
16
+ 'EXPIRED',
17
+ 'DECLINED',
18
+ 'CANCELLED'
19
+ ];
20
+ // NOTE: typically handling: "/api/webhooks/app/billing"
21
+ /**
22
+ * @param {any} [serverRuntimeConfig]
23
+ * @param {any} [data]
24
+ */
25
+ export const handleAppBilling = (serverRuntimeConfig, data) => {
26
+ const { returnResponse = true } = data !== null && data !== void 0 ? data : {};
27
+ /**
28
+ * @param {any} req
29
+ * @param {any} res
30
+ */
31
+ return async (req, res) => {
32
+ var _a, _b, _c, _d;
33
+ /**
34
+ * @param {any} [body]
35
+ * @param {any} [statusCode]
36
+ */
37
+ const handleResponse = (body, statusCode = 200) => {
38
+ if (returnResponse) {
39
+ return res.status(statusCode).json(body);
40
+ }
41
+ return { statusCode, body };
42
+ };
43
+ try {
44
+ const { 'x-shop-domain': shopDomain, 'x-shopify-shop-domain': shopifyShopDomain, 'x-shopify-shop': shop = shopifyShopDomain !== null && shopifyShopDomain !== void 0 ? shopifyShopDomain : shopDomain, } = (_a = req.headers) !== null && _a !== void 0 ? _a : {};
45
+ if (stringEmpty(shop)) {
46
+ return handleResponse({ handler: 'billing', error: 'Missing shop' });
47
+ }
48
+ const helpers = await getShopifyHelpers(serverRuntimeConfig, { shop });
49
+ if (!helpers.shopify) {
50
+ throw new Error('Shopify API is not initialized');
51
+ }
52
+ const { shopify, loadSession, updateSession, getVerifiedClients } = helpers;
53
+ // TODO: explore getVerificationHelpers?
54
+ const rawBody = await ensureRawBody(req);
55
+ const _e = await shopify.webhooks.validate({
56
+ rawBody,
57
+ rawRequest: req,
58
+ rawResponse: res,
59
+ }), { valid } = _e, webhookCheck = __rest(_e, ["valid"]);
60
+ if (valid) {
61
+ try {
62
+ let message = 'ignored';
63
+ const [eventType, { status } = {}] = Object.entries(safeParse(rawBody || '{}'))[0];
64
+ if ((eventType === BILLING_EVENT_TYPES.SUBSCRIPTION) && TERMINAL_STATUSES.includes(status)) {
65
+ const { Graphql } = (_b = await getVerifiedClients()) !== null && _b !== void 0 ? _b : {};
66
+ if (!Graphql) {
67
+ throw new Error('Missing shop');
68
+ }
69
+ const installationSubscriptionsResponse = await Graphql.request(APP_QUERY.INSTALLATION_SUBSCRIPTIONS);
70
+ const installationSubscriptions = (_d = (_c = installationSubscriptionsResponse === null || installationSubscriptionsResponse === void 0 ? void 0 : installationSubscriptionsResponse.data) === null || _c === void 0 ? void 0 : _c.currentAppInstallation) === null || _d === void 0 ? void 0 : _d.activeSubscriptions;
71
+ // NOTE this ensure all subscriptions are cancelled when in terminal status
72
+ await Promise.all(ensureArray(installationSubscriptions).map((subscription) => (Graphql.request(APP_QUERY.CANCEL_APP_SUBSCRIPTION, {
73
+ variables: {
74
+ id: subscription === null || subscription === void 0 ? void 0 : subscription.id,
75
+ },
76
+ }))));
77
+ message = 'deleted';
78
+ }
79
+ if ((serverRuntimeConfig.SHOPIFY_RUN_MODE === 'review') && !TERMINAL_STATUSES.includes(status)) {
80
+ const { id: sessionId } = await loadSession();
81
+ // NOTE: billingStatus is a custom field we add when we're in review mode to handle reauth billing correctly
82
+ await updateSession(sessionId, { billingStatus: status });
83
+ }
84
+ return handleResponse({ handler: 'billing', message });
85
+ }
86
+ catch (error) {
87
+ return handleResponse({ handler: 'billing', error: error === null || error === void 0 ? void 0 : error.message });
88
+ }
89
+ }
90
+ console.warn('Unable to validate billing webhook', webhookCheck);
91
+ return handleResponse({ handler: 'billing', error: 'Unable to validate billing webhook' });
92
+ }
93
+ catch (error) {
94
+ console.error('Error handling billing', error);
95
+ return handleResponse({ handler: 'billing', error: 'Error handling billing' });
96
+ }
97
+ };
98
+ };
@@ -0,0 +1,162 @@
1
+ import { InvalidHmacError, InvalidOAuthError, GraphqlQueryError, CookieNotFound, BillingError, } from '@shopify/shopify-api';
2
+ import { getPrepareWebhookHandlers, normalizeShopifyId, getShopifyHelpers, } from '../../utils/index.js';
3
+ import { isTrue, appendQuery, stringEmpty, ensureArray, ensureString, stringNotEmpty, ensureLeadingSlash, PAGE_ROUTE_NAMES, API_ROUTE_NAMES, ERROR_CODES, } from '../../../shared/index.js';
4
+ /* NOTE: typically handling:
5
+ * "/api/auth/online/callback"
6
+ * "/api/auth/offline/callback"
7
+ * "/api/auth/online/callback/reauth"
8
+ * "/api/auth/offline/callback/reauth"
9
+ */
10
+ // TODO: delete this for legacy auth
11
+ /**
12
+ * @param {any} [serverRuntimeConfig]
13
+ * @param {any} [data]
14
+ */
15
+ export const handleCallback = (serverRuntimeConfig, data) => {
16
+ const { API_PATHS, PAGE_PATHS, PAYMENT_CONFIG, } = serverRuntimeConfig;
17
+ const { returnResponse = true } = data !== null && data !== void 0 ? data : {};
18
+ const prepareWebhookHandlers = getPrepareWebhookHandlers(serverRuntimeConfig);
19
+ /**
20
+ * @param {any} req
21
+ * @param {any} res
22
+ * @param {any} params
23
+ */
24
+ return async (req, res, params) => {
25
+ var _a, _b;
26
+ const errorPath = PAGE_PATHS[PAGE_ROUTE_NAMES.ERROR];
27
+ const { isOnline, reAuth } = params !== null && params !== void 0 ? params : {};
28
+ const { shop, host, state } = (_a = req.query) !== null && _a !== void 0 ? _a : {};
29
+ const authPayload = { rawRequest: req, rawResponse: res };
30
+ /**
31
+ * @param {any} [body]
32
+ * @param {any} [statusCode]
33
+ */
34
+ const handleResponse = (body, statusCode = 307) => {
35
+ if (returnResponse) {
36
+ return res.redirect(statusCode, body);
37
+ }
38
+ return { statusCode, body };
39
+ };
40
+ if (stringEmpty(shop) || stringEmpty(host)) {
41
+ // HMMM: why is this a 303 (rather than a 302/307 or or anything)??
42
+ return handleResponse(`${errorPath}?code=${ERROR_CODES.INVALID_CALLBACK}`, 303);
43
+ }
44
+ let redirectUrl = `${errorPath}?code=invalid_callback_response`;
45
+ let shopify;
46
+ try {
47
+ const helpers = await getShopifyHelpers(serverRuntimeConfig, { shop });
48
+ if (!helpers.shopify) {
49
+ throw new Error('Shopify API is not initialized');
50
+ }
51
+ const { shopify: _shopify, loadAppData, storeSession: encryptStoreSession, ensureBilling: handleEnsureBilling, } = helpers;
52
+ shopify = _shopify;
53
+ const { session: _session } = await shopify.auth.callback(authPayload);
54
+ const session = _session === null || _session === void 0 ? void 0 : _session.toObject();
55
+ const offlineAuthRoute = API_PATHS[API_ROUTE_NAMES.AUTH_OFFLINE];
56
+ const shouldProcessOfflineAuth = (isOnline && offlineAuthRoute);
57
+ const shouldRegisterWebhooks = (!isTrue(session === null || session === void 0 ? void 0 : session.isOnline) || !offlineAuthRoute);
58
+ const shouldCheckBilling = PAYMENT_CONFIG === null || PAYMENT_CONFIG === void 0 ? void 0 : PAYMENT_CONFIG.required;
59
+ // NOTE: handleEnsureBilling below handles calling encryptStoreSession for offline session when shouldCheckBilling
60
+ if (isOnline || !shouldCheckBilling) {
61
+ await encryptStoreSession(session);
62
+ }
63
+ // NOTE: return early to process offline auth & callback (as webhooks cannot use the online one to make API calls)
64
+ if (shouldProcessOfflineAuth) {
65
+ // TODO: explore objectToQuerystring
66
+ const queryParams = new URLSearchParams(/** @type {any} */ ({ host, shop, state, reAuth, isOnline: false })).toString();
67
+ return handleResponse(`${offlineAuthRoute}?${queryParams}`);
68
+ }
69
+ if (shouldRegisterWebhooks) {
70
+ let appId;
71
+ try {
72
+ const { appInfo } = await loadAppData();
73
+ appId = normalizeShopifyId(appInfo === null || appInfo === void 0 ? void 0 : appInfo.id).id;
74
+ // eslint-disable-next-line no-empty
75
+ }
76
+ catch (_ignore) {
77
+ console.info('Ignoring error loading appData', _ignore);
78
+ }
79
+ const webhookHandlers = prepareWebhookHandlers({ appId });
80
+ shopify.webhooks.addHandlers(webhookHandlers);
81
+ // NOTE: webhooks cannot use an online session to make API calls to Shopify (but not all webooks may be doing that for all apps)
82
+ await shopify.webhooks.register({ session: /** @type {any} */ (session) });
83
+ }
84
+ const embeddedAppUrl = await shopify.auth.getEmbeddedAppUrl(authPayload);
85
+ // NOTE this is the final/successful return URL
86
+ redirectUrl = `${embeddedAppUrl}${ensureLeadingSlash(PAGE_PATHS[PAGE_ROUTE_NAMES.SUCCESS])}`;
87
+ if (shouldCheckBilling) {
88
+ // NOTE: handleEnsureBilling handles calling encryptStoreSession for offline session when shouldCheckBilling
89
+ const confirmationUrl = await handleEnsureBilling(session, { reAuth });
90
+ if (stringNotEmpty(confirmationUrl)) {
91
+ redirectUrl = confirmationUrl;
92
+ }
93
+ }
94
+ // NOTE confirm that state can be appended to the confirmationUrl,
95
+ // otherwise we can append to the redirectUrl above the billing check
96
+ redirectUrl = appendQuery(redirectUrl, new URLSearchParams({ state }).toString());
97
+ }
98
+ catch (error) {
99
+ console.error('Error handling callback route', error);
100
+ const errorMessage = 'Error handling callback route';
101
+ let baseErrorUrl = errorPath;
102
+ if (shopify) {
103
+ try {
104
+ const embeddedAppUrl = await shopify.auth.getEmbeddedAppUrl(authPayload);
105
+ if (stringNotEmpty(embeddedAppUrl)) {
106
+ baseErrorUrl = `${embeddedAppUrl}${ensureLeadingSlash(errorPath)}`;
107
+ }
108
+ // eslint-disable-next-line no-empty
109
+ }
110
+ catch (_ignore) { }
111
+ }
112
+ switch (true) {
113
+ // TODO implement a custom SessionNotFound error
114
+ case error instanceof InvalidHmacError: {
115
+ console.warn(`${errorMessage}. InvalidHmac Error ${error.message}`);
116
+ // HMMMM: not sure
117
+ const authOnlinePath = API_PATHS[API_ROUTE_NAMES.AUTH_ONLINE];
118
+ const authPath = isOnline
119
+ ? authOnlinePath
120
+ : API_PATHS[API_ROUTE_NAMES.AUTH_OFFLINE] || authOnlinePath;
121
+ // TODO: explore objectToQuerystring
122
+ const queryParams = new URLSearchParams(Object.assign({ host, shop }, state && { state })).toString();
123
+ redirectUrl = `${authPath}?${queryParams}`;
124
+ break;
125
+ }
126
+ case error instanceof CookieNotFound: {
127
+ console.warn(`${errorMessage}. This may occur if Cookie is already deleted. CookieNotFound Error ${error.message}`);
128
+ // HMMMM: not sure
129
+ const authOnlinePath = API_PATHS[API_ROUTE_NAMES.AUTH_ONLINE];
130
+ const authPath = isOnline
131
+ ? authOnlinePath
132
+ : API_PATHS[API_ROUTE_NAMES.AUTH_OFFLINE] || authOnlinePath;
133
+ // TODO: explore objectToQuerystring
134
+ const queryParams = new URLSearchParams(Object.assign({ host, shop }, state && { state })).toString();
135
+ redirectUrl = `${authPath}?${queryParams}`;
136
+ break;
137
+ }
138
+ case error instanceof BillingError: {
139
+ console.error(`${errorMessage}. Billing Error ${error.message}`, error.errorData);
140
+ redirectUrl = `${baseErrorUrl}?code=${ERROR_CODES.BILLING}`;
141
+ break;
142
+ }
143
+ case error instanceof GraphqlQueryError: {
144
+ const errorMessages = ensureArray((_b = error.response) === null || _b === void 0 ? void 0 : _b.errors).map(({ message }) => ensureString(message));
145
+ console.error(`${errorMessage}. GraphqlQuery Error ${errorMessages.join('. ')}`);
146
+ redirectUrl = `${baseErrorUrl}?code=${ERROR_CODES.QUERY}`;
147
+ break;
148
+ }
149
+ case error instanceof InvalidOAuthError: {
150
+ console.error(`${errorMessage}. InvalidOAuth Error`, error);
151
+ redirectUrl = `${baseErrorUrl}?code=${ERROR_CODES.OAUTH}`;
152
+ break;
153
+ }
154
+ default: {
155
+ console.error(errorMessage, error);
156
+ redirectUrl = `${baseErrorUrl}?code=${ERROR_CODES.CALLBACK}`;
157
+ }
158
+ }
159
+ }
160
+ return handleResponse(redirectUrl);
161
+ };
162
+ };
@@ -0,0 +1,17 @@
1
+ export * from './auth.js';
2
+ export * from './billing.js';
3
+ export * from './callback.js';
4
+ export * from './uninstall.js';
5
+ export * from './verifiable/index.js';
6
+ export * from './webhook.js';
7
+ // Next's build-time analyzer only accepts a literal for `export const config` — on next 15
8
+ // any call/identifier/spread is a fatal "Unsupported node type" (next 12 only warned). The
9
+ // one indirection it accepts is a named re-export, so consumers on next 15+ should use:
10
+ // export { API_CONFIG as config } from '@agnostack/next-shopify/lib'
11
+ // Only runtime-read keys (api.*) belong here — static keys (runtime, maxDuration, amp)
12
+ // would be silently ignored in that shape.
13
+ export const API_CONFIG = Object.freeze({
14
+ api: Object.freeze({
15
+ bodyParser: false,
16
+ }),
17
+ });
File without changes
@@ -0,0 +1,75 @@
1
+ var __rest = (this && this.__rest) || function (s, e) {
2
+ var t = {};
3
+ for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p) && e.indexOf(p) < 0)
4
+ t[p] = s[p];
5
+ if (s != null && typeof Object.getOwnPropertySymbols === "function")
6
+ for (var i = 0, p = Object.getOwnPropertySymbols(s); i < p.length; i++) {
7
+ if (e.indexOf(p[i]) < 0 && Object.prototype.propertyIsEnumerable.call(s, p[i]))
8
+ t[p[i]] = s[p[i]];
9
+ }
10
+ return t;
11
+ };
12
+ import { ensureRawBody } from '@agnostack/verifyd';
13
+ import { getShopifyHelpers } from '../../utils/index.js';
14
+ import { stringEmpty, arrayNotEmpty, } from '../../../shared/index.js';
15
+ // NOTE: typically handling: "/api/webhooks/app/uninstalled"
16
+ /**
17
+ * @param {any} [serverRuntimeConfig]
18
+ * @param {any} [data]
19
+ */
20
+ export const handleAppUninstalled = (serverRuntimeConfig, data) => {
21
+ const { returnResponse = true } = data !== null && data !== void 0 ? data : {};
22
+ /**
23
+ * @param {any} req
24
+ * @param {any} res
25
+ */
26
+ return async (req, res) => {
27
+ var _a;
28
+ /**
29
+ * @param {any} [body]
30
+ * @param {any} [statusCode]
31
+ */
32
+ const handleResponse = (body, statusCode = 200) => {
33
+ if (returnResponse) {
34
+ return res.status(statusCode).json(body);
35
+ }
36
+ return { statusCode, body };
37
+ };
38
+ try {
39
+ const { 'x-shop-domain': shopDomain, 'x-shopify-shop-domain': shopifyShopDomain, 'x-shopify-shop': shop = shopifyShopDomain !== null && shopifyShopDomain !== void 0 ? shopifyShopDomain : shopDomain, } = (_a = req.headers) !== null && _a !== void 0 ? _a : {};
40
+ if (stringEmpty(shop)) {
41
+ return handleResponse({ handler: 'uninstall', error: 'Missing shop' });
42
+ }
43
+ const { shopify, findSessionIds, deleteSessions } = await getShopifyHelpers(serverRuntimeConfig, { shop });
44
+ if (!shopify) {
45
+ throw new Error('Shopify API is not initialized');
46
+ }
47
+ // TODO: explore getVerificationHelpers?
48
+ const rawBody = await ensureRawBody(req);
49
+ const _b = await shopify.webhooks.validate({
50
+ rawBody,
51
+ rawRequest: req,
52
+ rawResponse: res,
53
+ }), { valid } = _b, webhookCheck = __rest(_b, ["valid"]);
54
+ if (valid) {
55
+ try {
56
+ const sessionsIds = await findSessionIds(shop);
57
+ if (arrayNotEmpty(sessionsIds)) {
58
+ await deleteSessions(sessionsIds);
59
+ }
60
+ return handleResponse({ handler: 'uninstall', message: 'success' });
61
+ }
62
+ catch (error) {
63
+ console.error('Error handling uninstall', error);
64
+ return handleResponse({ handler: 'uninstall', error: (error === null || error === void 0 ? void 0 : error.message) || 'Error handling uninstall' });
65
+ }
66
+ }
67
+ console.warn('Unable to validate uninstall webhook', webhookCheck);
68
+ return handleResponse({ handler: 'uninstall', error: 'Unable to validate uninstall webhook' });
69
+ }
70
+ catch (error) {
71
+ console.error('Error handling uninstall', error);
72
+ return handleResponse({ handler: 'uninstall', error: 'Error handling uninstall' });
73
+ }
74
+ };
75
+ };
@@ -0,0 +1,37 @@
1
+ import { stringEmpty, APP_QUERY, TRANSFORM } from '../../../../shared/index.js';
2
+ import { handleGraph } from './graph.js';
3
+ // NOTE: typically handling: "/api/admin/installation/[appId]" (called by getUseAppInstallation)
4
+ /**
5
+ * @param {any} [serverRuntimeConfig]
6
+ * @param {any} [data]
7
+ */
8
+ export const handleLoadAppInstallation = (serverRuntimeConfig, data) => {
9
+ const _handleGraph = handleGraph(serverRuntimeConfig, data);
10
+ const { returnResponse = true } = data !== null && data !== void 0 ? data : {};
11
+ /**
12
+ * @param {any} req
13
+ * @param {any} res
14
+ * @param {any} params
15
+ */
16
+ return async (req, res, params) => {
17
+ const { appId } = params !== null && params !== void 0 ? params : {};
18
+ /**
19
+ * @param {any} [body]
20
+ * @param {any} [statusCode]
21
+ */
22
+ const handleResponse = (body, statusCode = 200) => {
23
+ if (returnResponse) {
24
+ return res.status(statusCode).json(body);
25
+ }
26
+ return { statusCode, body };
27
+ };
28
+ if (stringEmpty(appId)) {
29
+ return handleResponse({ message: 'Missing required param' }, 412);
30
+ }
31
+ return _handleGraph(req, res, {
32
+ query: APP_QUERY.INSTALLATION,
33
+ variables: { namespace: `${appId}-metafields` },
34
+ transform: TRANSFORM.APP_QUERY,
35
+ });
36
+ };
37
+ };
@@ -0,0 +1,149 @@
1
+ var __rest = (this && this.__rest) || function (s, e) {
2
+ var t = {};
3
+ for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p) && e.indexOf(p) < 0)
4
+ t[p] = s[p];
5
+ if (s != null && typeof Object.getOwnPropertySymbols === "function")
6
+ for (var i = 0, p = Object.getOwnPropertySymbols(s); i < p.length; i++) {
7
+ if (e.indexOf(p[i]) < 0 && Object.prototype.propertyIsEnumerable.call(s, p[i]))
8
+ t[p[i]] = s[p[i]];
9
+ }
10
+ return t;
11
+ };
12
+ import { BillingError, GraphqlQueryError } from '@shopify/shopify-api';
13
+ import { getVerificationHelpers, VerificationError } from '@agnostack/verifyd';
14
+ import { getShopifyHelpers, getAuthErrorResponse } from '../../../utils/index.js';
15
+ import { isType, safeParse, stringEmpty, ensureString, ensureObject, ensureArray, arrayNotEmpty, objectNotEmpty, querystringToObject, ShopifyAuthenticationError, ShopifySessionAuthenticationError, } from '../../../../shared/index.js';
16
+ /**
17
+ * @param {any} [serverRuntimeConfig]
18
+ * @param {any} [data]
19
+ */
20
+ export const handleGraph = (serverRuntimeConfig, data) => {
21
+ const _getVerificationHelpers = getVerificationHelpers(serverRuntimeConfig);
22
+ const { returnResponse = true } = data !== null && data !== void 0 ? data : {};
23
+ /**
24
+ * @param {any} req
25
+ * @param {any} res
26
+ * @param {any} params
27
+ */
28
+ return async (req, res, params) => {
29
+ var _a, _b, _c;
30
+ /**
31
+ * @param {any} [body]
32
+ * @param {any} [statusCode]
33
+ */
34
+ const handleResponse = (body, statusCode = 200) => {
35
+ if (returnResponse) {
36
+ return res.status(statusCode).json(body);
37
+ }
38
+ return { statusCode, body };
39
+ };
40
+ let requestBody;
41
+ let processResponse;
42
+ try {
43
+ ({ requestBody, processResponse } = await _getVerificationHelpers(req));
44
+ }
45
+ catch (error) {
46
+ if (error instanceof VerificationError) {
47
+ return handleResponse(Object.assign({ message: error.message }, error.data), error.code);
48
+ }
49
+ return handleResponse({ message: 'Verification error' }, 403);
50
+ }
51
+ const _d = (_a = req.query) !== null && _a !== void 0 ? _a : {}, { shop, apiVersion } = _d, _query = __rest(_d, ["shop", "apiVersion"]);
52
+ const _e = params !== null && params !== void 0 ? params : safeParse(requestBody || '{}'), { isOnline, query: graphQuery, variables: graphVariables, transform = (/** @type {any} */ _data) => _data } = _e, graphParams = __rest(_e, ["isOnline", "query", "variables", "transform"]);
53
+ let query = _query;
54
+ if (isType(query, 'string')) {
55
+ query = querystringToObject(query);
56
+ }
57
+ if (stringEmpty(shop) || stringEmpty(graphQuery)) {
58
+ return handleResponse({ message: 'Invalid arguments' }, 400);
59
+ }
60
+ let statusCode;
61
+ let response;
62
+ let headers;
63
+ let message = 'Error handling query call';
64
+ try {
65
+ const helpers = await getShopifyHelpers(serverRuntimeConfig, { shop });
66
+ if (!helpers.shopify) {
67
+ throw new Error('Shopify API is not initialized');
68
+ }
69
+ const { getVerifiedClients } = helpers;
70
+ const { Graphql } = (_b = await getVerifiedClients({ req, res, shop, apiVersion, isOnline })) !== null && _b !== void 0 ? _b : {};
71
+ if (!Graphql) {
72
+ throw new Error('Missing shop');
73
+ }
74
+ response = await Graphql.request(graphQuery, Object.assign(Object.assign(Object.assign({}, objectNotEmpty(graphVariables) && { variables: graphVariables }), objectNotEmpty(query) && { query }), graphParams));
75
+ statusCode = 200;
76
+ }
77
+ catch (error) {
78
+ statusCode = 500;
79
+ // TODO: clean up to share between rest and graph (pr at least keep in sync)
80
+ switch (true) {
81
+ case error instanceof ShopifySessionAuthenticationError: {
82
+ console.error(`${message}. Authentication Error.`, error);
83
+ statusCode = 401;
84
+ message = 'Unauthorized';
85
+ // HMMMM, should this be:
86
+ // message = `${message}. Authentication Error - ${errroResponse.message}.`
87
+ headers = {
88
+ // TODO ensure this is working once upgraded to AppBridge v4
89
+ 'X-Shopify-Retry-Invalid-Session-Request': '1',
90
+ };
91
+ break;
92
+ }
93
+ case error instanceof ShopifyAuthenticationError: {
94
+ console.error(`${message}. Authentication Error.`, error);
95
+ const errorResponse = getAuthErrorResponse(serverRuntimeConfig, { error, shop });
96
+ statusCode = errorResponse.statusCode;
97
+ message = errorResponse.message;
98
+ // HMMMM, should this be:
99
+ // message = `${message}. Authentication Error - ${errroResponse.message}.`
100
+ headers = errorResponse.headers;
101
+ break;
102
+ }
103
+ case error instanceof BillingError: {
104
+ // HMMMM, should this be:
105
+ // message = `${message}. Billing Error - ${error.message}.`
106
+ console.error(`${message}. Billing Error - ${error.message}.`, error.errorData);
107
+ statusCode = 402;
108
+ if (error === null || error === void 0 ? void 0 : error.message) {
109
+ message = error.message;
110
+ }
111
+ break;
112
+ }
113
+ case error instanceof GraphqlQueryError: {
114
+ const errorMessages = ensureArray((_c = error.response) === null || _c === void 0 ? void 0 : _c.errors).map(({ message }) => ensureString(message));
115
+ console.error(`${message}. ${errorMessages.join('. ')}.`);
116
+ // HMMMM, should this be:
117
+ // message = `${message}. ${errorMessages.join('. ')}.`
118
+ // TODO: why are these not sending back different error codes?
119
+ break;
120
+ }
121
+ default: {
122
+ console.error(`${message}. HandleGraph Error - ${error.message}.`);
123
+ break;
124
+ }
125
+ }
126
+ }
127
+ try {
128
+ Object.entries(ensureObject(headers)).forEach(([key, value]) => {
129
+ res.setHeader(key, value);
130
+ });
131
+ if (!(response === null || response === void 0 ? void 0 : response.data)) {
132
+ return handleResponse({ message }, statusCode);
133
+ }
134
+ const responseBody = ensureObject(response.data);
135
+ const [_responseData] = Object.values(responseBody);
136
+ const _f = ensureObject(_responseData), { userErrors } = _f, responseData = __rest(_f, ["userErrors"]);
137
+ if (arrayNotEmpty(userErrors)) {
138
+ const errorMessages = ensureArray(userErrors).map(({ message }) => ensureString(message));
139
+ return handleResponse({ message: `${message}. ${errorMessages.join('. ')}.` }, 500);
140
+ }
141
+ // NOTE: sending in both to transform in case someone needs the userErrors and/or multi-value query
142
+ return handleResponse(await processResponse(transform(responseData, responseBody)), statusCode);
143
+ }
144
+ catch (error) {
145
+ console.error(`${message}. Transformation Error.`, error);
146
+ return handleResponse({ message }, 500);
147
+ }
148
+ };
149
+ };
@@ -0,0 +1,4 @@
1
+ export * from './appInstallation.js';
2
+ export * from './graph.js';
3
+ export * from './proxy.js';
4
+ export * from './token.js';