@shopify/shopify-app-remix 4.1.1 → 4.2.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 (276) hide show
  1. package/dist/cjs/react/components/AppProvider/AppProvider.js.map +1 -1
  2. package/dist/cjs/react/components/AppProxyForm/AppProxyForm.js +4 -3
  3. package/dist/cjs/react/components/AppProxyForm/AppProxyForm.js.map +1 -1
  4. package/dist/cjs/react/components/AppProxyLink/AppProxyLink.js +4 -3
  5. package/dist/cjs/react/components/AppProxyLink/AppProxyLink.js.map +1 -1
  6. package/dist/cjs/react/components/AppProxyProvider/AppProxyProvider.js.map +1 -1
  7. package/dist/cjs/server/authenticate/admin/helpers/redirect.js +2 -2
  8. package/dist/cjs/server/authenticate/admin/helpers/redirect.js.map +1 -1
  9. package/dist/cjs/server/authenticate/admin/strategies/auth-code-flow.js.map +1 -1
  10. package/dist/cjs/server/authenticate/admin/strategies/merchant-custom-app.js.map +1 -1
  11. package/dist/cjs/server/authenticate/admin/strategies/token-exchange.js.map +1 -1
  12. package/dist/cjs/server/authenticate/webhooks/authenticate.js +34 -10
  13. package/dist/cjs/server/authenticate/webhooks/authenticate.js.map +1 -1
  14. package/dist/cjs/server/future/flags.js.map +1 -1
  15. package/dist/cjs/server/shopify-app.js +1 -0
  16. package/dist/cjs/server/shopify-app.js.map +1 -1
  17. package/dist/cjs/server/version.js +1 -1
  18. package/dist/cjs/server/version.js.map +1 -1
  19. package/dist/esm/react/components/AppProvider/AppProvider.mjs.map +1 -1
  20. package/dist/esm/react/components/AppProxyForm/AppProxyForm.mjs +5 -4
  21. package/dist/esm/react/components/AppProxyForm/AppProxyForm.mjs.map +1 -1
  22. package/dist/esm/react/components/AppProxyLink/AppProxyLink.mjs +5 -4
  23. package/dist/esm/react/components/AppProxyLink/AppProxyLink.mjs.map +1 -1
  24. package/dist/esm/react/components/AppProxyProvider/AppProxyProvider.mjs.map +1 -1
  25. package/dist/esm/server/authenticate/admin/helpers/redirect.mjs +2 -2
  26. package/dist/esm/server/authenticate/admin/helpers/redirect.mjs.map +1 -1
  27. package/dist/esm/server/authenticate/admin/strategies/auth-code-flow.mjs.map +1 -1
  28. package/dist/esm/server/authenticate/admin/strategies/merchant-custom-app.mjs.map +1 -1
  29. package/dist/esm/server/authenticate/admin/strategies/token-exchange.mjs.map +1 -1
  30. package/dist/esm/server/authenticate/webhooks/authenticate.mjs +35 -11
  31. package/dist/esm/server/authenticate/webhooks/authenticate.mjs.map +1 -1
  32. package/dist/esm/server/future/flags.mjs.map +1 -1
  33. package/dist/esm/server/shopify-app.mjs +1 -0
  34. package/dist/esm/server/shopify-app.mjs.map +1 -1
  35. package/dist/esm/server/version.mjs +1 -1
  36. package/dist/esm/server/version.mjs.map +1 -1
  37. package/dist/ts/react/components/AppProvider/AppProvider.d.ts +5 -1
  38. package/dist/ts/react/components/AppProvider/AppProvider.d.ts.map +1 -1
  39. package/dist/ts/react/components/AppProxyForm/AppProxyForm.d.ts +2 -1
  40. package/dist/ts/react/components/AppProxyForm/AppProxyForm.d.ts.map +1 -1
  41. package/dist/ts/react/components/AppProxyLink/AppProxyLink.d.ts +2 -1
  42. package/dist/ts/react/components/AppProxyLink/AppProxyLink.d.ts.map +1 -1
  43. package/dist/ts/react/components/AppProxyProvider/AppProxyProvider.d.ts +5 -1
  44. package/dist/ts/react/components/AppProxyProvider/AppProxyProvider.d.ts.map +1 -1
  45. package/dist/ts/server/authenticate/admin/billing/types.d.ts +4 -0
  46. package/dist/ts/server/authenticate/admin/billing/types.d.ts.map +1 -1
  47. package/dist/ts/server/authenticate/admin/scope/types.d.ts +2 -1
  48. package/dist/ts/server/authenticate/admin/scope/types.d.ts.map +1 -1
  49. package/dist/ts/server/authenticate/admin/strategies/auth-code-flow.d.ts.map +1 -1
  50. package/dist/ts/server/authenticate/admin/strategies/merchant-custom-app.d.ts.map +1 -1
  51. package/dist/ts/server/authenticate/admin/strategies/token-exchange.d.ts.map +1 -1
  52. package/dist/ts/server/authenticate/admin/types.d.ts +10 -0
  53. package/dist/ts/server/authenticate/admin/types.d.ts.map +1 -1
  54. package/dist/ts/server/authenticate/flow/types.d.ts +4 -0
  55. package/dist/ts/server/authenticate/flow/types.d.ts.map +1 -1
  56. package/dist/ts/server/authenticate/fulfillment-service/types.d.ts +4 -0
  57. package/dist/ts/server/authenticate/fulfillment-service/types.d.ts.map +1 -1
  58. package/dist/ts/server/authenticate/public/appProxy/types.d.ts +4 -0
  59. package/dist/ts/server/authenticate/public/appProxy/types.d.ts.map +1 -1
  60. package/dist/ts/server/authenticate/public/checkout/types.d.ts +4 -0
  61. package/dist/ts/server/authenticate/public/checkout/types.d.ts.map +1 -1
  62. package/dist/ts/server/authenticate/public/customer-account/types.d.ts +4 -0
  63. package/dist/ts/server/authenticate/public/customer-account/types.d.ts.map +1 -1
  64. package/dist/ts/server/authenticate/public/extension/types.d.ts.map +1 -1
  65. package/dist/ts/server/authenticate/webhooks/authenticate.d.ts.map +1 -1
  66. package/dist/ts/server/authenticate/webhooks/types.d.ts +36 -3
  67. package/dist/ts/server/authenticate/webhooks/types.d.ts.map +1 -1
  68. package/dist/ts/server/boundary/error.d.ts +1 -1
  69. package/dist/ts/server/boundary/error.d.ts.map +1 -1
  70. package/dist/ts/server/clients/admin/types.d.ts +4 -0
  71. package/dist/ts/server/clients/admin/types.d.ts.map +1 -1
  72. package/dist/ts/server/clients/storefront/types.d.ts +4 -0
  73. package/dist/ts/server/clients/storefront/types.d.ts.map +1 -1
  74. package/dist/ts/server/config-types.d.ts.map +1 -1
  75. package/dist/ts/server/future/flags.d.ts +6 -0
  76. package/dist/ts/server/future/flags.d.ts.map +1 -1
  77. package/dist/ts/server/shopify-app.d.ts +1 -0
  78. package/dist/ts/server/shopify-app.d.ts.map +1 -1
  79. package/dist/ts/server/unauthenticated/admin/types.d.ts +4 -0
  80. package/dist/ts/server/unauthenticated/admin/types.d.ts.map +1 -1
  81. package/dist/ts/server/unauthenticated/storefront/types.d.ts +4 -0
  82. package/dist/ts/server/unauthenticated/storefront/types.d.ts.map +1 -1
  83. package/dist/ts/server/version.d.ts +1 -1
  84. package/dist/tsconfig.build.tsbuildinfo +1 -1
  85. package/package.json +19 -15
  86. package/src/react/components/AppProvider/AppProvider.tsx +151 -0
  87. package/src/react/components/AppProvider/index.ts +1 -0
  88. package/src/react/components/AppProxyForm/AppProxyForm.tsx +85 -0
  89. package/src/react/components/AppProxyForm/index.ts +1 -0
  90. package/src/react/components/AppProxyLink/AppProxyLink.tsx +61 -0
  91. package/src/react/components/AppProxyLink/index.ts +1 -0
  92. package/src/react/components/AppProxyProvider/AppProxyProvider.tsx +104 -0
  93. package/src/react/components/AppProxyProvider/index.ts +5 -0
  94. package/src/react/components/RemixPolarisLink.tsx +14 -0
  95. package/src/react/components/index.ts +4 -0
  96. package/src/react/const.ts +2 -0
  97. package/src/react/index.ts +1 -0
  98. package/src/server/__test-helpers/const.ts +14 -0
  99. package/src/server/__test-helpers/expect-admin-api-client.ts +51 -0
  100. package/src/server/__test-helpers/expect-begin-auth-redirect.ts +22 -0
  101. package/src/server/__test-helpers/expect-document-request-headers.ts +25 -0
  102. package/src/server/__test-helpers/expect-exit-iframe.ts +37 -0
  103. package/src/server/__test-helpers/expect-login-redirect.ts +8 -0
  104. package/src/server/__test-helpers/expect-storefront-api-client.ts +49 -0
  105. package/src/server/__test-helpers/expect-token-refresh.ts +188 -0
  106. package/src/server/__test-helpers/get-hmac.ts +7 -0
  107. package/src/server/__test-helpers/get-jwt.ts +10 -0
  108. package/src/server/__test-helpers/get-thrown-error.ts +19 -0
  109. package/src/server/__test-helpers/get-thrown-response.ts +17 -0
  110. package/src/server/__test-helpers/index.ts +21 -0
  111. package/src/server/__test-helpers/mock-graphql-request.ts +60 -0
  112. package/src/server/__test-helpers/request-mock.ts +169 -0
  113. package/src/server/__test-helpers/setup-embedded-flow.ts +30 -0
  114. package/src/server/__test-helpers/setup-fetch-flow.ts +29 -0
  115. package/src/server/__test-helpers/setup-non-embedded-flow.ts +31 -0
  116. package/src/server/__test-helpers/setup-valid-request.ts +78 -0
  117. package/src/server/__test-helpers/setup-valid-session.ts +28 -0
  118. package/src/server/__test-helpers/sign-request-cookie.ts +32 -0
  119. package/src/server/__test-helpers/test-config.ts +56 -0
  120. package/src/server/adapters/node/index.ts +13 -0
  121. package/src/server/adapters/vercel/index.ts +13 -0
  122. package/src/server/authenticate/admin/authenticate.admin.doc.example.ts +34 -0
  123. package/src/server/authenticate/admin/authenticate.ts +232 -0
  124. package/src/server/authenticate/admin/billing/cancel.ts +38 -0
  125. package/src/server/authenticate/admin/billing/check.ts +41 -0
  126. package/src/server/authenticate/admin/billing/create-usage-record.ts +36 -0
  127. package/src/server/authenticate/admin/billing/helpers.ts +42 -0
  128. package/src/server/authenticate/admin/billing/index.ts +6 -0
  129. package/src/server/authenticate/admin/billing/request.ts +64 -0
  130. package/src/server/authenticate/admin/billing/require.ts +58 -0
  131. package/src/server/authenticate/admin/billing/types.ts +617 -0
  132. package/src/server/authenticate/admin/billing/update-usage-subscription-capped-amount.ts +55 -0
  133. package/src/server/authenticate/admin/helpers/begin-auth.ts +17 -0
  134. package/src/server/authenticate/admin/helpers/create-admin-api-context.ts +20 -0
  135. package/src/server/authenticate/admin/helpers/ensure-app-is-embedded-if-required.ts +18 -0
  136. package/src/server/authenticate/admin/helpers/ensure-session-token-search-param-if-required.ts +25 -0
  137. package/src/server/authenticate/admin/helpers/handle-client-error.ts +45 -0
  138. package/src/server/authenticate/admin/helpers/index.ts +14 -0
  139. package/src/server/authenticate/admin/helpers/redirect-to-auth-page.ts +28 -0
  140. package/src/server/authenticate/admin/helpers/redirect-to-bounce-page.ts +23 -0
  141. package/src/server/authenticate/admin/helpers/redirect-to-install-page.ts +53 -0
  142. package/src/server/authenticate/admin/helpers/redirect-to-shopify-or-app-root.ts +21 -0
  143. package/src/server/authenticate/admin/helpers/redirect-with-app-bridge-headers.ts +13 -0
  144. package/src/server/authenticate/admin/helpers/redirect-with-exitiframe.ts +28 -0
  145. package/src/server/authenticate/admin/helpers/redirect.ts +164 -0
  146. package/src/server/authenticate/admin/helpers/render-app-bridge.ts +46 -0
  147. package/src/server/authenticate/admin/helpers/trigger-after-auth-hook.ts +29 -0
  148. package/src/server/authenticate/admin/helpers/validate-redirect-url.ts +67 -0
  149. package/src/server/authenticate/admin/helpers/validate-shop-and-host-params.ts +46 -0
  150. package/src/server/authenticate/admin/scope/client/fetch-scopes-details.ts +43 -0
  151. package/src/server/authenticate/admin/scope/client/revoke-scopes.ts +38 -0
  152. package/src/server/authenticate/admin/scope/factory.ts +21 -0
  153. package/src/server/authenticate/admin/scope/query.ts +51 -0
  154. package/src/server/authenticate/admin/scope/request.ts +32 -0
  155. package/src/server/authenticate/admin/scope/revoke.ts +48 -0
  156. package/src/server/authenticate/admin/scope/types.ts +127 -0
  157. package/src/server/authenticate/admin/strategies/auth-code-flow.ts +332 -0
  158. package/src/server/authenticate/admin/strategies/merchant-custom-app.ts +65 -0
  159. package/src/server/authenticate/admin/strategies/token-exchange.ts +188 -0
  160. package/src/server/authenticate/admin/strategies/types.ts +29 -0
  161. package/src/server/authenticate/admin/types.ts +226 -0
  162. package/src/server/authenticate/const.ts +15 -0
  163. package/src/server/authenticate/flow/authenticate.flow.doc.example.ts +39 -0
  164. package/src/server/authenticate/flow/authenticate.ts +68 -0
  165. package/src/server/authenticate/flow/types.ts +89 -0
  166. package/src/server/authenticate/fulfillment-service/authenticate.fulfillment-service.doc.example.ts +52 -0
  167. package/src/server/authenticate/fulfillment-service/authenticate.ts +82 -0
  168. package/src/server/authenticate/fulfillment-service/types.ts +86 -0
  169. package/src/server/authenticate/helpers/add-response-headers.ts +43 -0
  170. package/src/server/authenticate/helpers/app-bridge-url.ts +10 -0
  171. package/src/server/authenticate/helpers/ensure-cors-headers.ts +38 -0
  172. package/src/server/authenticate/helpers/get-session-token-header.ts +11 -0
  173. package/src/server/authenticate/helpers/get-shop-from-request.ts +4 -0
  174. package/src/server/authenticate/helpers/idempotent-promise-handler.ts +45 -0
  175. package/src/server/authenticate/helpers/index.ts +10 -0
  176. package/src/server/authenticate/helpers/invalidate-access-token.ts +17 -0
  177. package/src/server/authenticate/helpers/reject-bot-request.ts +27 -0
  178. package/src/server/authenticate/helpers/respond-to-invalid-session-token.ts +28 -0
  179. package/src/server/authenticate/helpers/respond-to-options-request.ts +26 -0
  180. package/src/server/authenticate/helpers/validate-session-token.ts +40 -0
  181. package/src/server/authenticate/login/login.ts +53 -0
  182. package/src/server/authenticate/public/appProxy/authenticate.public.app-proxy.doc.example.ts +27 -0
  183. package/src/server/authenticate/public/appProxy/authenticate.ts +150 -0
  184. package/src/server/authenticate/public/appProxy/types.ts +219 -0
  185. package/src/server/authenticate/public/checkout/authenticate.public.checkout.doc.example.offers.ts +21 -0
  186. package/src/server/authenticate/public/checkout/authenticate.public.checkout.doc.example.ts +17 -0
  187. package/src/server/authenticate/public/checkout/authenticate.ts +13 -0
  188. package/src/server/authenticate/public/checkout/types.ts +69 -0
  189. package/src/server/authenticate/public/customer-account/authenticate.public.customerAccount.doc.example.offers.ts +36 -0
  190. package/src/server/authenticate/public/customer-account/authenticate.public.customerAccount.doc.example.ts +18 -0
  191. package/src/server/authenticate/public/customer-account/authenticate.ts +13 -0
  192. package/src/server/authenticate/public/customer-account/types.ts +69 -0
  193. package/src/server/authenticate/public/extension/authenticate.ts +54 -0
  194. package/src/server/authenticate/public/extension/types.ts +11 -0
  195. package/src/server/authenticate/public/factory.ts +24 -0
  196. package/src/server/authenticate/public/index.ts +1 -0
  197. package/src/server/authenticate/public/pos/authenticate.ts +8 -0
  198. package/src/server/authenticate/public/pos/types.ts +65 -0
  199. package/src/server/authenticate/public/types.ts +96 -0
  200. package/src/server/authenticate/webhooks/authenticate.ts +105 -0
  201. package/src/server/authenticate/webhooks/authenticate.webhooks.doc.example.ts +43 -0
  202. package/src/server/authenticate/webhooks/index.ts +1 -0
  203. package/src/server/authenticate/webhooks/register.ts +48 -0
  204. package/src/server/authenticate/webhooks/types.ts +267 -0
  205. package/src/server/boundary/error.tsx +14 -0
  206. package/src/server/boundary/headers.ts +15 -0
  207. package/src/server/boundary/index.ts +39 -0
  208. package/src/server/boundary/types.ts +4 -0
  209. package/src/server/clients/admin/factory.ts +22 -0
  210. package/src/server/clients/admin/graphql.ts +39 -0
  211. package/src/server/clients/admin/index.ts +2 -0
  212. package/src/server/clients/admin/types.ts +124 -0
  213. package/src/server/clients/index.ts +2 -0
  214. package/src/server/clients/storefront/factory.ts +32 -0
  215. package/src/server/clients/storefront/index.ts +2 -0
  216. package/src/server/clients/storefront/types.ts +82 -0
  217. package/src/server/clients/types.ts +47 -0
  218. package/src/server/config-types.ts +309 -0
  219. package/src/server/errors.ts +3 -0
  220. package/src/server/future/flags.ts +68 -0
  221. package/src/server/helpers/create-or-load-offline-session.ts +19 -0
  222. package/src/server/helpers/ensure-offline-token-is-not-expired.ts +32 -0
  223. package/src/server/helpers/ensure-valid-offline-session.ts +15 -0
  224. package/src/server/helpers/index.ts +4 -0
  225. package/src/server/helpers/refresh-token.ts +35 -0
  226. package/src/server/index.ts +24 -0
  227. package/src/server/override-logger.ts +42 -0
  228. package/src/server/shopify-app.ts +216 -0
  229. package/src/server/test-helpers/index.ts +1 -0
  230. package/src/server/test-helpers/test-config.ts +79 -0
  231. package/src/server/types-contexts.ts +44 -0
  232. package/src/server/types.ts +507 -0
  233. package/src/server/unauthenticated/admin/factory.ts +23 -0
  234. package/src/server/unauthenticated/admin/index.ts +1 -0
  235. package/src/server/unauthenticated/admin/types.ts +83 -0
  236. package/src/server/unauthenticated/storefront/factory.ts +28 -0
  237. package/src/server/unauthenticated/storefront/index.ts +1 -0
  238. package/src/server/unauthenticated/storefront/types.ts +109 -0
  239. package/src/server/unauthenticated/types.ts +65 -0
  240. package/src/server/version.ts +1 -0
  241. package/dist/ts/react/components/AppProvider/AppProvider.doc.d.ts +0 -4
  242. package/dist/ts/react/components/AppProvider/AppProvider.doc.d.ts.map +0 -1
  243. package/dist/ts/react/components/AppProxyForm/AppProxyForm.doc.d.ts +0 -4
  244. package/dist/ts/react/components/AppProxyForm/AppProxyForm.doc.d.ts.map +0 -1
  245. package/dist/ts/react/components/AppProxyLink/AppProxyLink.doc.d.ts +0 -4
  246. package/dist/ts/react/components/AppProxyLink/AppProxyLink.doc.d.ts.map +0 -1
  247. package/dist/ts/react/components/AppProxyProvider/AppProxyProvider.doc.d.ts +0 -4
  248. package/dist/ts/react/components/AppProxyProvider/AppProxyProvider.doc.d.ts.map +0 -1
  249. package/dist/ts/server/authenticate/admin/authenticate.admin.doc.d.ts +0 -4
  250. package/dist/ts/server/authenticate/admin/authenticate.admin.doc.d.ts.map +0 -1
  251. package/dist/ts/server/authenticate/admin/billing/authenticate.admin.billing.doc.d.ts +0 -4
  252. package/dist/ts/server/authenticate/admin/billing/authenticate.admin.billing.doc.d.ts.map +0 -1
  253. package/dist/ts/server/authenticate/admin/scope/authenticate.admin.scopes.doc.d.ts +0 -4
  254. package/dist/ts/server/authenticate/admin/scope/authenticate.admin.scopes.doc.d.ts.map +0 -1
  255. package/dist/ts/server/authenticate/flow/authenticate.flow.doc.d.ts +0 -4
  256. package/dist/ts/server/authenticate/flow/authenticate.flow.doc.d.ts.map +0 -1
  257. package/dist/ts/server/authenticate/fulfillment-service/authenticate.fulfillment-service.doc.d.ts +0 -4
  258. package/dist/ts/server/authenticate/fulfillment-service/authenticate.fulfillment-service.doc.d.ts.map +0 -1
  259. package/dist/ts/server/authenticate/public/appProxy/authenticate.public.app-proxy.doc.d.ts +0 -4
  260. package/dist/ts/server/authenticate/public/appProxy/authenticate.public.app-proxy.doc.d.ts.map +0 -1
  261. package/dist/ts/server/authenticate/public/checkout/authenticate.public.checkout.doc.d.ts +0 -4
  262. package/dist/ts/server/authenticate/public/checkout/authenticate.public.checkout.doc.d.ts.map +0 -1
  263. package/dist/ts/server/authenticate/public/customer-account/authenticate.public.customerAccount.doc.d.ts +0 -4
  264. package/dist/ts/server/authenticate/public/customer-account/authenticate.public.customerAccount.doc.d.ts.map +0 -1
  265. package/dist/ts/server/authenticate/webhooks/authenticate.webhooks.doc.d.ts +0 -4
  266. package/dist/ts/server/authenticate/webhooks/authenticate.webhooks.doc.d.ts.map +0 -1
  267. package/dist/ts/server/clients/admin/authenticate.admin.api.doc.d.ts +0 -4
  268. package/dist/ts/server/clients/admin/authenticate.admin.api.doc.d.ts.map +0 -1
  269. package/dist/ts/server/clients/storefront/authenticate.storefront.api.doc.d.ts +0 -4
  270. package/dist/ts/server/clients/storefront/authenticate.storefront.api.doc.d.ts.map +0 -1
  271. package/dist/ts/server/shopify-app.doc.d.ts +0 -4
  272. package/dist/ts/server/shopify-app.doc.d.ts.map +0 -1
  273. package/dist/ts/server/unauthenticated/admin/unauthenticated.admin.doc.d.ts +0 -4
  274. package/dist/ts/server/unauthenticated/admin/unauthenticated.admin.doc.d.ts.map +0 -1
  275. package/dist/ts/server/unauthenticated/storefront/unauthenticated.storefront.doc.d.ts +0 -4
  276. package/dist/ts/server/unauthenticated/storefront/unauthenticated.storefront.doc.d.ts.map +0 -1
@@ -0,0 +1,267 @@
1
+ import {Session, WebhookTypeValue} from '@shopify/shopify-api';
2
+
3
+ import type {AdminApiContext} from '../../clients';
4
+
5
+ export interface RegisterWebhooksOptions {
6
+ /**
7
+ * The Shopify session used to register webhooks using the Admin API.
8
+ */
9
+ session: Session;
10
+ }
11
+
12
+ interface Context<Topics = string | number | symbol> {
13
+ /**
14
+ * The API version used for the webhook.
15
+ *
16
+ * @example
17
+ * <caption>Webhook API version.</caption>
18
+ * <description>Get the API version used for webhook request.</description>
19
+ * ```ts
20
+ * // /app/routes/webhooks.tsx
21
+ * import { ActionFunctionArgs } from "@remix-run/node";
22
+ * import { authenticate } from "../shopify.server";
23
+ *
24
+ * export const action = async ({ request }: ActionFunctionArgs) => {
25
+ * const { apiVersion } = await authenticate.webhook(request);
26
+ * return new Response();
27
+ * };
28
+ * ```
29
+ */
30
+ apiVersion: string;
31
+
32
+ /**
33
+ * The shop where the webhook was triggered.
34
+ *
35
+ * @example
36
+ * <caption>Webhook shop.</caption>
37
+ * <description>Get the shop that triggered a webhook.</description>
38
+ * ```ts
39
+ * // /app/routes/webhooks.tsx
40
+ * import { ActionFunctionArgs } from "@remix-run/node";
41
+ * import { authenticate } from "../shopify.server";
42
+ *
43
+ * export const action = async ({ request }: ActionFunctionArgs) => {
44
+ * const { shop } = await authenticate.webhook(request);
45
+ * return new Response();
46
+ * };
47
+ * ```
48
+ */
49
+ shop: string;
50
+
51
+ /**
52
+ * The topic of the webhook.
53
+ *
54
+ * @example
55
+ * <caption>Webhook topic.</caption>
56
+ * <description>Get the event topic for the webhook.</description>
57
+ * ```ts
58
+ * // /app/routes/webhooks.tsx
59
+ * import { ActionFunctionArgs } from "@remix-run/node";
60
+ * import { authenticate } from "../shopify.server";
61
+ *
62
+ * export const action = async ({ request }: ActionFunctionArgs) => {
63
+ * const { topic } = await authenticate.webhook(request);
64
+ *
65
+ * switch (topic) {
66
+ * case "APP_UNINSTALLED":
67
+ * // Do something when the app is uninstalled.
68
+ * break;
69
+ * }
70
+ *
71
+ * return new Response();
72
+ * };
73
+ * ```
74
+ */
75
+ topic: Topics;
76
+
77
+ /**
78
+ * A unique ID for the webhook. This is the idempotency key — useful to keep track of which
79
+ * events your app has already processed.
80
+ *
81
+ * @example
82
+ * <caption>Webhook ID.</caption>
83
+ * <description>Get the webhook ID.</description>
84
+ * ```ts
85
+ * // /app/routes/webhooks.tsx
86
+ * import { ActionFunctionArgs } from "@remix-run/node";
87
+ * import { authenticate } from "../shopify.server";
88
+ *
89
+ * export const action = async ({ request }: ActionFunctionArgs) => {
90
+ * const { webhookId } = await authenticate.webhook(request);
91
+ * return new Response();
92
+ * };
93
+ * ```
94
+ */
95
+ webhookId: string;
96
+
97
+ /**
98
+ * The payload from the webhook request.
99
+ *
100
+ * @example
101
+ * <caption>Webhook payload.</caption>
102
+ * <description>Get the request's POST payload.</description>
103
+ * ```ts
104
+ * // /app/routes/webhooks.tsx
105
+ * import { ActionFunctionArgs } from "@remix-run/node";
106
+ * import { authenticate } from "../shopify.server";
107
+ *
108
+ * export const action = async ({ request }: ActionFunctionArgs) => {
109
+ * const { payload } = await authenticate.webhook(request);
110
+ * return new Response();
111
+ * };
112
+ * ```
113
+ */
114
+ payload: Record<string, any>;
115
+
116
+ /**
117
+ * The type of webhook: 'webhooks' for traditional webhooks or 'events' for events webhooks.
118
+ */
119
+ webhookType: WebhookTypeValue;
120
+
121
+ /**
122
+ * The sub-topic of the webhook. Only available for traditional webhooks.
123
+ *
124
+ * @example
125
+ * <caption>Webhook sub-topic.</caption>
126
+ * <description>Get the webhook sub-topic.</description>
127
+ * ```ts
128
+ * // /app/routes/webhooks.tsx
129
+ * import { ActionFunctionArgs } from "@remix-run/node";
130
+ * import { authenticate } from "../shopify.server";
131
+ *
132
+ * export const action = async ({ request }: ActionFunctionArgs) => {
133
+ * const { subTopic } = await authenticate.webhook(request);
134
+ * return new Response();
135
+ * };
136
+ * ```
137
+ *
138
+ */
139
+ subTopic?: string;
140
+
141
+ /**
142
+ * The name assigned to the webhook subscription. Only available for traditional webhooks.
143
+ */
144
+ name?: string;
145
+
146
+ /**
147
+ * The handle for the webhook subscription. Only available for events webhooks.
148
+ */
149
+ handle?: string;
150
+
151
+ /**
152
+ * The action type: 'create', 'update', or 'delete'. Only available for events webhooks.
153
+ */
154
+ action?: string;
155
+
156
+ /**
157
+ * The GID of the resource that triggered the webhook. Only available for events webhooks.
158
+ */
159
+ resourceId?: string;
160
+
161
+ /**
162
+ * The timestamp when the webhook was triggered.
163
+ */
164
+ triggeredAt?: string;
165
+
166
+ /**
167
+ * The unique event identifier.
168
+ */
169
+ eventId?: string;
170
+ }
171
+
172
+ export interface WebhookContextWithoutSession<
173
+ Topics = string | number | symbol,
174
+ > extends Context<Topics> {
175
+ session: undefined;
176
+ admin: undefined;
177
+ }
178
+
179
+ export interface WebhookContextWithSession<
180
+ Topics = string | number | symbol,
181
+ > extends Context<Topics> {
182
+ /**
183
+ * A session with an offline token for the shop.
184
+ *
185
+ * Returned only if there is a session for the shop.
186
+ * Webhook requests can trigger after an app is uninstalled
187
+ * If the app is already uninstalled, the session may be undefined.
188
+ * Therefore, you should check for the session before using it.
189
+ *
190
+ * @example
191
+ * <caption>Protecting against uninstalled apps.</caption>
192
+ * ```ts
193
+ * // /app/routes/webhooks.tsx
194
+ * import type { ActionFunctionArgs } from "@remix-run/node";
195
+ * import { authenticate } from "~/shopify.server";
196
+
197
+ * export const action = async ({ request }: ActionFunctionArgs) => {
198
+ * const { session } = await authenticate.webhook(request);
199
+ *
200
+ * // Webhook requests can trigger after an app is uninstalled
201
+ * // If the app is already uninstalled, the session may be undefined.
202
+ * if (!session) {
203
+ * throw new Response();
204
+ * }
205
+ *
206
+ * // Handle webhook request
207
+ * console.log("Received webhook webhook");
208
+ *
209
+ * return new Response();
210
+ * };
211
+ * ```
212
+ */
213
+ session: Session;
214
+
215
+ /**
216
+ * An admin context for the webhook.
217
+ *
218
+ * Returned only if there is a session for the shop.
219
+ *
220
+ * @example
221
+ * <caption>Webhook admin context.</caption>
222
+ * <description>Use the `admin` object in the context to interact with the Admin API.</description>
223
+ * ```ts
224
+ * // /app/routes/webhooks.tsx
225
+ * import { ActionFunctionArgs } from "@remix-run/node";
226
+ * import { authenticate } from "../shopify.server";
227
+ *
228
+ * export async function action({ request }: ActionFunctionArgs) {
229
+ * const { admin } = await authenticate.webhook(request);
230
+ *
231
+ * // Webhook requests can trigger after an app is uninstalled
232
+ * // If the app is already uninstalled, the session may be undefined.
233
+ * if (!session) {
234
+ * throw new Response();
235
+ * }
236
+ *
237
+ * const response = await admin?.graphql(
238
+ * `#graphql
239
+ * mutation populateProduct($input: ProductInput!) {
240
+ * productCreate(input: $input) {
241
+ * product {
242
+ * id
243
+ * }
244
+ * }
245
+ * }`,
246
+ * { variables: { input: { title: "Product Name" } } }
247
+ * );
248
+ *
249
+ * const productData = await response.json();
250
+ * return json({ data: productData.data });
251
+ * }
252
+ * ```
253
+ */
254
+ admin: AdminApiContext;
255
+ }
256
+
257
+ export type WebhookContext<Topics = string | number | symbol> =
258
+ | WebhookContextWithoutSession<Topics>
259
+ | WebhookContextWithSession<Topics>;
260
+
261
+ /**
262
+ * Verifies requests coming from Shopify webhooks.
263
+ * @publicDocs
264
+ */
265
+ export type AuthenticateWebhook<Topics = string | number | symbol> = (
266
+ request: Request,
267
+ ) => Promise<WebhookContext<Topics>>;
@@ -0,0 +1,14 @@
1
+ export function errorBoundary(error: any) {
2
+ if (
3
+ error.constructor.name === 'ErrorResponse' ||
4
+ error.constructor.name === 'ErrorResponseImpl'
5
+ ) {
6
+ return (
7
+ <div
8
+ dangerouslySetInnerHTML={{__html: error.data || 'Handling response'}}
9
+ />
10
+ );
11
+ }
12
+
13
+ throw error;
14
+ }
@@ -0,0 +1,15 @@
1
+ import {HeadersArgs} from '@remix-run/server-runtime';
2
+
3
+ export function headersBoundary(headers: HeadersArgs): Headers {
4
+ const {parentHeaders, loaderHeaders, actionHeaders, errorHeaders} = headers;
5
+
6
+ if (errorHeaders && Array.from(errorHeaders.entries()).length > 0) {
7
+ return errorHeaders;
8
+ }
9
+
10
+ return new Headers([
11
+ ...(parentHeaders ? Array.from(parentHeaders.entries()) : []),
12
+ ...(loaderHeaders ? Array.from(loaderHeaders.entries()) : []),
13
+ ...(actionHeaders ? Array.from(actionHeaders.entries()) : []),
14
+ ]);
15
+ }
@@ -0,0 +1,39 @@
1
+ import {headersBoundary} from './headers';
2
+ import {errorBoundary} from './error';
3
+
4
+ /**
5
+ * A collection of functions that handle the necessary code for error boundaries in routes using authenticate.admin.
6
+ */
7
+ export const boundary = {
8
+ /**
9
+ * A function that handles errors or thrown responses.
10
+ *
11
+ * @example
12
+ * <caption>Catching errors in a route</caption>
13
+ * ```ts
14
+ * // /app/routes/admin/widgets.ts
15
+ * import { boundary } from "@shopify/shopify-app-remix/server";
16
+ *
17
+ * export function ErrorBoundary() {
18
+ * return boundary.error(useRouteError());
19
+ * }
20
+ * ```
21
+ */
22
+ error: errorBoundary,
23
+
24
+ /**
25
+ * A function that sets the appropriate document response headers.
26
+ *
27
+ * @example
28
+ * <caption>Catching errors in a route</caption>
29
+ * ```ts
30
+ * // /app/routes/admin/widgets.ts
31
+ * import { boundary } from "@shopify/shopify-app-remix/server";
32
+ *
33
+ * export const headers = (headersArgs) => {
34
+ * return boundary.headers(headersArgs);
35
+ * };
36
+ * ```
37
+ */
38
+ headers: headersBoundary,
39
+ };
@@ -0,0 +1,4 @@
1
+ import {HeadersArgs} from '@remix-run/server-runtime';
2
+
3
+ export type ErrorBoundary = (error: unknown) => string | never;
4
+ export type HeadersBoundary = (headers: HeadersArgs) => Headers;
@@ -0,0 +1,22 @@
1
+ import {Session} from '@shopify/shopify-api';
2
+
3
+ import {BasicParams} from '../../types';
4
+
5
+ import {graphqlClientFactory} from './graphql';
6
+ import type {AdminApiContext} from './types';
7
+
8
+ interface AdminClientOptions {
9
+ params: BasicParams;
10
+ session: Session;
11
+ handleClientError?: (error: any) => Promise<void>;
12
+ }
13
+
14
+ export function adminClientFactory({
15
+ params,
16
+ handleClientError,
17
+ session,
18
+ }: AdminClientOptions): AdminApiContext {
19
+ return {
20
+ graphql: graphqlClientFactory({params, session, handleClientError}),
21
+ };
22
+ }
@@ -0,0 +1,39 @@
1
+ import {AdminOperations} from '@shopify/admin-api-client';
2
+
3
+ import {GraphQLClient} from '../types';
4
+
5
+ import {AdminClientOptions} from './types';
6
+
7
+ // eslint-disable-next-line no-warning-comments
8
+ // TODO: This is actually just a call through to the Shopify API client, but with a different API. We should eventually
9
+ // move this over to the library layer.
10
+ export function graphqlClientFactory({
11
+ params,
12
+ handleClientError,
13
+ session,
14
+ }: AdminClientOptions): GraphQLClient<AdminOperations> {
15
+ return async function query(operation, options) {
16
+ const client = new params.api.clients.Graphql({
17
+ session,
18
+ apiVersion: options?.apiVersion,
19
+ });
20
+
21
+ try {
22
+ // We convert the incoming response to a Response object to bring this client closer to the Remix client.
23
+ const apiResponse = await client.request(operation, {
24
+ variables: options?.variables,
25
+ retries: options?.tries ? options.tries - 1 : 0,
26
+ headers: options?.headers,
27
+ signal: options?.signal,
28
+ });
29
+
30
+ return new Response(JSON.stringify(apiResponse));
31
+ } catch (error) {
32
+ if (handleClientError) {
33
+ throw await handleClientError({error, params, session});
34
+ }
35
+
36
+ throw error;
37
+ }
38
+ };
39
+ }
@@ -0,0 +1,2 @@
1
+ export * from './factory';
2
+ export * from './types';
@@ -0,0 +1,124 @@
1
+ import {AdminOperations} from '@shopify/admin-api-client';
2
+ import {Session} from '@shopify/shopify-api';
3
+
4
+ import {BasicParams} from '../../types';
5
+ import {GraphQLClient} from '../types';
6
+
7
+ export interface AdminClientOptions {
8
+ params: BasicParams;
9
+ session: Session;
10
+ handleClientError?: HandleAdminClientError;
11
+ }
12
+
13
+ export type HandleAdminClientError = (arg: {
14
+ error: any;
15
+ params: BasicParams;
16
+ session: Session;
17
+ }) => Promise<void>;
18
+
19
+ /**
20
+ * Provides utilities that apps can use to make requests to the Admin API.
21
+ * @publicDocs
22
+ */
23
+ export interface AdminApiContext {
24
+ /**
25
+ * Methods for interacting with the Shopify Admin GraphQL API
26
+ *
27
+ * {@link https://shopify.dev/docs/api/admin-graphql}
28
+ * {@link https://github.com/Shopify/shopify-app-js/blob/main/packages/apps/shopify-api/docs/reference/clients/Graphql.md}
29
+ *
30
+ * @example
31
+ * <caption>Querying the GraphQL API.</caption>
32
+ * <description>Use `admin.graphql` to make query / mutation requests.</description>
33
+ * ```ts
34
+ * // /app/routes/**\/*.ts
35
+ * import { ActionFunctionArgs } from "@remix-run/node";
36
+ * import { authenticate } from "../shopify.server";
37
+ *
38
+ * export const action = async ({ request }: ActionFunctionArgs) => {
39
+ * const { admin } = await authenticate.admin(request);
40
+ *
41
+ * const response = await admin.graphql(
42
+ * `#graphql
43
+ * mutation populateProduct($input: ProductInput!) {
44
+ * productCreate(input: $input) {
45
+ * product {
46
+ * id
47
+ * }
48
+ * }
49
+ * }`,
50
+ * {
51
+ * variables: {
52
+ * input: { title: "Product Name" },
53
+ * },
54
+ * },
55
+ * );
56
+ *
57
+ * const productData = await response.json();
58
+ * return json({
59
+ * productId: productData.data?.productCreate?.product?.id,
60
+ * });
61
+ * }
62
+ * ```
63
+ *
64
+ * ```ts
65
+ * // /app/shopify.server.ts
66
+ * import { shopifyApp } from "@shopify/shopify-app-remix/server";
67
+ *
68
+ * const shopify = shopifyApp({
69
+ * // ...
70
+ * });
71
+ * export default shopify;
72
+ * export const authenticate = shopify.authenticate;
73
+ * ```
74
+ *
75
+ * @example
76
+ * <caption>Handling GraphQL errors.</caption>
77
+ * <description>Catch `GraphqlQueryError` errors to see error messages from the API.</description>
78
+ * ```ts
79
+ * // /app/routes/**\/*.ts
80
+ * import { ActionFunctionArgs } from "@remix-run/node";
81
+ * import { authenticate } from "../shopify.server";
82
+ *
83
+ * export const action = async ({ request }: ActionFunctionArgs) => {
84
+ * const { admin } = await authenticate.admin(request);
85
+ *
86
+ * try {
87
+ * const response = await admin.graphql(
88
+ * `#graphql
89
+ * query incorrectQuery {
90
+ * products(first: 10) {
91
+ * nodes {
92
+ * not_a_field
93
+ * }
94
+ * }
95
+ * }`,
96
+ * );
97
+ *
98
+ * return json({ data: await response.json() });
99
+ * } catch (error) {
100
+ * if (error instanceof GraphqlQueryError) {
101
+ * // error.body.errors:
102
+ * // { graphQLErrors: [
103
+ * // { message: "Field 'not_a_field' doesn't exist on type 'Product'" }
104
+ * // ] }
105
+ * return json({ errors: error.body?.errors }, { status: 500 });
106
+ * }
107
+ * return json({ message: "An error occurred" }, { status: 500 });
108
+ * }
109
+ * }
110
+ * ```
111
+ *
112
+ * ```ts
113
+ * // /app/shopify.server.ts
114
+ * import { shopifyApp } from "@shopify/shopify-app-remix/server";
115
+ *
116
+ * const shopify = shopifyApp({
117
+ * // ...
118
+ * });
119
+ * export default shopify;
120
+ * export const authenticate = shopify.authenticate;
121
+ * ```
122
+ */
123
+ graphql: GraphQLClient<AdminOperations>;
124
+ }
@@ -0,0 +1,2 @@
1
+ export * from './admin';
2
+ export * from './storefront';
@@ -0,0 +1,32 @@
1
+ import {Session} from '@shopify/shopify-api';
2
+
3
+ import {BasicParams} from '../../types';
4
+
5
+ import type {StorefrontContext} from '.';
6
+
7
+ export function storefrontClientFactory({
8
+ params,
9
+ session,
10
+ }: {
11
+ params: BasicParams;
12
+ session: Session;
13
+ }): StorefrontContext {
14
+ const {api} = params;
15
+
16
+ return {
17
+ graphql: async (query, options = {}) => {
18
+ const client = new api.clients.Storefront({
19
+ session,
20
+ apiVersion: options.apiVersion,
21
+ });
22
+
23
+ const apiResponse = await client.request(query, {
24
+ variables: options?.variables,
25
+ retries: options?.tries ? options.tries - 1 : 0,
26
+ headers: options?.headers,
27
+ });
28
+
29
+ return new Response(JSON.stringify(apiResponse));
30
+ },
31
+ };
32
+ }
@@ -0,0 +1,2 @@
1
+ export * from './factory';
2
+ export * from './types';
@@ -0,0 +1,82 @@
1
+ import {StorefrontOperations} from '@shopify/storefront-api-client';
2
+
3
+ import {GraphQLClient} from '../types';
4
+
5
+ /**
6
+ * Provides utilities that apps can use to make requests to the Storefront API.
7
+ * @publicDocs
8
+ */
9
+ export interface StorefrontContext {
10
+ /**
11
+ * Method for interacting with the Shopify Storefront GraphQL API
12
+ *
13
+ * If you're getting incorrect type hints in the Shopify template, follow [these instructions](https://github.com/Shopify/shopify-app-template-remix/tree/main#incorrect-graphql-hints).
14
+ *
15
+ * {@link https://shopify.dev/docs/api/storefront}
16
+ *
17
+ * @example
18
+ * <caption>Querying the GraphQL API.</caption>
19
+ * <description>Use `storefront.graphql` to make query / mutation requests.</description>
20
+ * ```ts
21
+ * // app/routes/**\/.ts
22
+ * import { json } from "@remix-run/node";
23
+ * import { authenticate } from "../shopify.server";
24
+ *
25
+ * export async function action({ request }: ActionFunctionArgs) {
26
+ * const { storefront } = await authenticate.public.appProxy(request);
27
+ *
28
+ * const response = await storefront.graphql(`{blogs(first: 10) { edges { node { id } } } }`);
29
+ *
30
+ * return json(await response.json());
31
+ * }
32
+ * ```
33
+ *
34
+ * @example
35
+ * <caption>Handling GraphQL errors.</caption>
36
+ * <description>Catch `GraphqlQueryError` errors to see error messages from the API.</description>
37
+ * ```ts
38
+ * // /app/routes/**\/*.ts
39
+ * import { ActionFunctionArgs } from "@remix-run/node";
40
+ * import { authenticate } from "../shopify.server";
41
+ *
42
+ * export const action = async ({ request }: ActionFunctionArgs) => {
43
+ * const { storefront } = await authenticate.public.appProxy(request);
44
+ *
45
+ * try {
46
+ * const response = await storefront.graphql(
47
+ * `#graphql
48
+ * query incorrectQuery {
49
+ * products(first: 10) {
50
+ * nodes {
51
+ * not_a_field
52
+ * }
53
+ * }
54
+ * }`,
55
+ * );
56
+ *
57
+ * return json({ data: await response.json() });
58
+ * } catch (error) {
59
+ * if (error instanceof GraphqlQueryError) {
60
+ * // { errors: { graphQLErrors: [
61
+ * // { message: "Field 'not_a_field' doesn't exist on type 'Product'" }
62
+ * // ] } }
63
+ * return json({ errors: error.body?.errors }, { status: 500 });
64
+ * }
65
+ * return json({ message: "An error occurred" }, { status: 500 });
66
+ * }
67
+ * }
68
+ * ```
69
+ *
70
+ * ```ts
71
+ * // /app/shopify.server.ts
72
+ * import { shopifyApp } from "@shopify/shopify-app-remix/server";
73
+ *
74
+ * const shopify = shopifyApp({
75
+ * // ...
76
+ * });
77
+ * export default shopify;
78
+ * export const authenticate = shopify.authenticate;
79
+ * ```
80
+ */
81
+ graphql: GraphQLClient<StorefrontOperations>;
82
+ }