@agnostack/next-shopify 4.0.0-beta.1 → 4.0.0-canary.10

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