@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
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@shopify/shopify-app-remix",
3
- "version": "4.1.1",
3
+ "version": "4.2.1",
4
4
  "description": "Shopify Remix - to simplify the building of Shopify Apps with Remix",
5
5
  "engines": {
6
6
  "node": ">=20.10.0"
@@ -66,25 +66,25 @@
66
66
  "Storefront API"
67
67
  ],
68
68
  "devDependencies": {
69
- "@babel/preset-env": "^7.28.5",
70
- "@remix-run/node": "^2.17.2",
71
- "@remix-run/react": "^2.17.3",
72
- "@remix-run/testing": "^2.17.2",
73
- "@shopify/generate-docs": "^0.19.8",
69
+ "@babel/preset-env": "^7.29.7",
70
+ "@remix-run/node": "^2.17.5",
71
+ "@remix-run/react": "^2.17.5",
72
+ "@remix-run/testing": "^2.17.5",
73
+ "@shopify/generate-docs": "^1.2.0",
74
74
  "@shopify/polaris": "^12.18.0",
75
75
  "@shopify/react-testing": "^6.0.0",
76
- "@shopify/shopify-app-session-storage-memory": "^5.0.5",
77
- "@types/react": "^19.2.2",
76
+ "@shopify/shopify-app-session-storage-memory": "^6.0.1",
77
+ "@types/react": "^19.2.16",
78
78
  "jest-fetch-mock": "^3.0.3"
79
79
  },
80
80
  "dependencies": {
81
- "@remix-run/server-runtime": "^2.17.3",
82
- "@shopify/admin-api-client": "^1.1.1",
83
- "@shopify/shopify-api": "^12.3.0",
84
- "@shopify/shopify-app-session-storage": "^4.0.5",
85
- "@shopify/storefront-api-client": "^1.0.9",
81
+ "@remix-run/server-runtime": "^2.17.5",
82
+ "@shopify/admin-api-client": "^1.1.2",
83
+ "@shopify/shopify-api": "^13.1.0",
84
+ "@shopify/shopify-app-session-storage": "^5.0.1",
85
+ "@shopify/storefront-api-client": "^1.0.10",
86
86
  "compare-versions": "^6.1.1",
87
- "isbot": "^5.1.32"
87
+ "isbot": "^5.1.40"
88
88
  },
89
89
  "peerDependencies": {
90
90
  "@remix-run/node": "*",
@@ -102,6 +102,10 @@
102
102
  },
103
103
  "files": [
104
104
  "dist/*",
105
+ "src",
106
+ "!src/**/__tests__",
107
+ "!src/**/*.test.ts",
108
+ "!src/**/*.test.tsx",
105
109
  "!build/**/__tests__",
106
110
  "!tsconfig.tsbuildinfo",
107
111
  "!node_modules"
@@ -109,7 +113,7 @@
109
113
  "scripts": {
110
114
  "build-docs": "sh docs/build-docs.sh",
111
115
  "validate-docs": "sh ./docs/build-docs.sh isTest && pnpm compare-docs",
112
- "lint": "eslint . --ext .js,.ts",
116
+ "lint": "eslint .",
113
117
  "build": "pnpm rollup && pnpm tsc -p ./tsconfig.build.json",
114
118
  "tsc": "tsc",
115
119
  "test": "jest",
@@ -0,0 +1,151 @@
1
+ import {
2
+ AppProvider as PolarisAppProvider,
3
+ AppProviderProps as PolarisAppProviderProps,
4
+ } from '@shopify/polaris';
5
+ // This leads to some TS errors, but it does compile to something that works
6
+ // eslint-disable-next-line @typescript-eslint/ban-ts-comment
7
+ // @ts-ignore
8
+ import englishI18n from '@shopify/polaris/locales/en.json' with {type: 'json'};
9
+ import {useNavigate} from '@remix-run/react';
10
+ import {useEffect} from 'react';
11
+
12
+ import {APP_BRIDGE_URL} from '../../const';
13
+ import {RemixPolarisLink} from '../RemixPolarisLink';
14
+
15
+ /**
16
+ * Props for the `AppProvider` component.
17
+ * @publicDocs
18
+ */
19
+ export interface AppProviderProps extends Omit<
20
+ PolarisAppProviderProps,
21
+ 'linkComponent' | 'i18n'
22
+ > {
23
+ /**
24
+ * The API key for your Shopify app. This is the `Client ID` from the Partner Dashboard.
25
+ *
26
+ * When using the Shopify CLI, this is the `SHOPIFY_API_KEY` environment variable. If you're using the environment
27
+ * variable, then you need to pass it from the loader to the component.
28
+ */
29
+ apiKey: string;
30
+ /**
31
+ * Whether the app is loaded inside the Shopify Admin. Default is `true`.
32
+ *
33
+ * {@link https://shopify.dev/docs/apps/admin/embedded-app-home}
34
+ */
35
+ isEmbeddedApp?: boolean;
36
+ /**
37
+ * The internationalization (i18n) configuration for your Polaris provider.
38
+ *
39
+ * {@link https://polaris.shopify.com/components/utilities/app-provider}
40
+ */
41
+ i18n?: PolarisAppProviderProps['i18n'];
42
+ /**
43
+ * Used internally by Shopify. You don't need to set this.
44
+ * @private
45
+ */
46
+ __APP_BRIDGE_URL?: string;
47
+ }
48
+
49
+ /**
50
+ * Sets up the Polaris `AppProvider` and injects the App Bridge script.
51
+ *
52
+ * This component extends the [`AppProvider`](https://polaris.shopify.com/components/utilities/app-provider) component
53
+ * from Polaris, and accepts all of its props except for `linkComponent`, which is overridden to use the Remix `Link`
54
+ * component.
55
+ *
56
+ * {@link https://polaris.shopify.com/components/utilities/app-provider}
57
+ * {@link https://shopify.dev/tools/app-bridge}
58
+ *
59
+ * @example
60
+ * <caption>Set up AppProvider.</caption>
61
+ * <description>Wrap your app in the `AppProvider` component and pass in your API key.</description>
62
+ * ```ts
63
+ * // /app/routes/**\/*.ts
64
+ * import {authenticate} from '~/shopify.server';
65
+ * import {AppProvider} from '@shopify/shopify-app-remix/react';
66
+ *
67
+ * export async function loader({ request }) {
68
+ * await authenticate.admin(request);
69
+ *
70
+ * return json({ apiKey: process.env.SHOPIFY_API_KEY });
71
+ * }
72
+ *
73
+ * export default function App() {
74
+ * const { apiKey } = useLoaderData();
75
+ *
76
+ * return (
77
+ * <AppProvider isEmbeddedApp apiKey={apiKey}>
78
+ * <Outlet />
79
+ * </AppProvider>
80
+ * );
81
+ * }
82
+ * ```
83
+ *
84
+ * @example
85
+ * <caption>Localize Polaris components.</caption>
86
+ * <description>Pass in a different locale for Polaris to translate its components.</description>
87
+ * ```ts
88
+ * // /app/routes/**\/*.ts
89
+ * import {authenticate} from '~/shopify.server';
90
+ * import {AppProvider} from '@shopify/shopify-app-remix/react';
91
+ *
92
+ * export async function loader({ request }) {
93
+ * await authenticate.admin(request);
94
+ *
95
+ * return json({
96
+ * apiKey: process.env.SHOPIFY_API_KEY,
97
+ * polarisTranslations: require("@shopify/polaris/locales/fr.json"),
98
+ * });
99
+ * }
100
+ *
101
+ * export default function App() {
102
+ * const { apiKey, polarisTranslations } = useLoaderData();
103
+ *
104
+ * return (
105
+ * <AppProvider apiKey={apiKey} i18n={polarisTranslations}>
106
+ * <Outlet />
107
+ * </AppProvider>
108
+ * );
109
+ * }
110
+ * ```
111
+ */
112
+ export function AppProvider(props: AppProviderProps) {
113
+ const {
114
+ children,
115
+ apiKey,
116
+ i18n,
117
+ isEmbeddedApp = true,
118
+ __APP_BRIDGE_URL = APP_BRIDGE_URL,
119
+ ...polarisProps
120
+ } = props;
121
+
122
+ const navigate = useNavigate();
123
+
124
+ useEffect(() => {
125
+ const handleNavigate = (event: Event) => {
126
+ const href = (event.target as HTMLElement)?.getAttribute('href');
127
+ if (href) {
128
+ navigate(href);
129
+ }
130
+ };
131
+
132
+ addEventListener('shopify:navigate', handleNavigate);
133
+
134
+ return () => {
135
+ removeEventListener('shopify:navigate', handleNavigate);
136
+ };
137
+ }, [navigate]);
138
+
139
+ return (
140
+ <>
141
+ {isEmbeddedApp && <script src={__APP_BRIDGE_URL} data-api-key={apiKey} />}
142
+ <PolarisAppProvider
143
+ {...polarisProps}
144
+ linkComponent={RemixPolarisLink}
145
+ i18n={i18n || englishI18n}
146
+ >
147
+ {children}
148
+ </PolarisAppProvider>
149
+ </>
150
+ );
151
+ }
@@ -0,0 +1 @@
1
+ export * from './AppProvider';
@@ -0,0 +1,85 @@
1
+ import {forwardRef, useContext} from 'react';
2
+ import {Form, type FormProps} from '@remix-run/react';
3
+
4
+ import {AppProxyProviderContext} from '../AppProxyProvider';
5
+
6
+ export interface AppProxyFormProps extends FormProps {
7
+ action: string;
8
+ }
9
+
10
+ /**
11
+ * Sets up a Remix `<Form>` component that works when rendered on an app proxy page.
12
+ *
13
+ * Supports any properties accepted by the `<Form>` component.
14
+ *
15
+ * Because Remix doesn't support URL rewriting, any route using this component should <b>match the pathname of the proxy
16
+ * URL exactly</b>, and <b>end in a trailing slash</b> (e.g., `https://<shop>/apps/proxy/`), or set the Remix Form prop
17
+ * `navigate` to `false`.
18
+ *
19
+ * @example
20
+ * <caption>Render a form element in a proxied route.</caption>
21
+ * <description>Use an `AppProxyForm` within an `AppProxy` to create a form.</description>
22
+ * ```ts
23
+ * // /app/routes/apps/appProxy.ts
24
+ * import {
25
+ * AppProxyProvider,
26
+ * AppProxyForm,
27
+ * } from "@shopify/shopify-app-remix/react";
28
+ * import { authenticate } from "~/shopify.server";
29
+ *
30
+ * export async function loader({ request }) {
31
+ * await authenticate.public.appProxy(request);
32
+ *
33
+ * return json({ appUrl: process.env.SHOPIFY_APP_URL });
34
+ * }
35
+ *
36
+ * export async function action({ request }) {
37
+ * await authenticate.public.appProxy(request);
38
+ *
39
+ * const formData = await request.formData();
40
+ * const field = formData.get("field")?.toString();
41
+ *
42
+ * // Perform actions
43
+ * if (field) {
44
+ * console.log("Field:", field);
45
+ * }
46
+ *
47
+ * // Return JSON to the client
48
+ * return json({ message: "Success!" });
49
+ * }
50
+ *
51
+ * export default function App() {
52
+ * const { appUrl } = useLoaderData();
53
+ *
54
+ * return (
55
+ * <AppProxyProvider appUrl={appUrl}>
56
+ * <AppProxyForm action="/apps/appProxy" method="post">
57
+ * <input type="text" name="field" />
58
+ *
59
+ * <input type="submit" name="Submit" />
60
+ * </AppProxyForm>
61
+ * </AppProxyProvider>
62
+ * );
63
+ * }
64
+ * ```
65
+ * @publicDocs
66
+ */
67
+ export const AppProxyForm = forwardRef<HTMLFormElement, AppProxyFormProps>(
68
+ function AppProxyForm(props, ref) {
69
+ const context = useContext(AppProxyProviderContext);
70
+
71
+ if (!context) {
72
+ throw new Error(
73
+ 'AppProxyForm must be used within an AppProxyProvider component',
74
+ );
75
+ }
76
+
77
+ const {children, action, ...otherProps} = props;
78
+
79
+ return (
80
+ <Form action={context.formatUrl(action, false)} {...otherProps} ref={ref}>
81
+ {children}
82
+ </Form>
83
+ );
84
+ },
85
+ );
@@ -0,0 +1 @@
1
+ export {AppProxyForm, type AppProxyFormProps} from './AppProxyForm';
@@ -0,0 +1,61 @@
1
+ import {forwardRef, useContext} from 'react';
2
+
3
+ import {AppProxyProviderContext} from '../AppProxyProvider';
4
+
5
+ export interface AppProxyLinkProps extends React.DetailedHTMLProps<
6
+ React.AnchorHTMLAttributes<HTMLAnchorElement>,
7
+ HTMLAnchorElement
8
+ > {
9
+ href: string;
10
+ }
11
+
12
+ /**
13
+ * Sets up an `<a />` HTML element that works when rendered behind an app proxy.
14
+ *
15
+ * Supports any properties accepted by the `<a />` HTML element.
16
+ *
17
+ * @example
18
+ * <caption>Link to a different route.</caption>
19
+ * <description>Use an `AppProxyLink` within an `AppProxyProvider` to link to a different proxied route.</description>
20
+ * ```ts
21
+ * // /app/routes/**\/*.ts
22
+ * import {authenticate} from '~/shopify.server';
23
+ * import {AppProxyProvider, AppProxyLink} from '@shopify/shopify-app-remix/react';
24
+ *
25
+ * export async function loader({ request }) {
26
+ * await authenticate.public.appProxy(request);
27
+ *
28
+ * return json({ appUrl: process.env.SHOPIFY_APP_URL });
29
+ * }
30
+ *
31
+ * export default function App() {
32
+ * const { appUrl } = useLoaderData();
33
+ *
34
+ * return (
35
+ * <AppProxyProvider appUrl={appUrl}>
36
+ * <AppProxyLink href="/other-proxy-route">Link to another route</AppProxyLink>
37
+ * </AppProxyProvider>
38
+ * );
39
+ * }
40
+ * ```
41
+ * @publicDocs
42
+ */
43
+ export const AppProxyLink = forwardRef<HTMLAnchorElement, AppProxyLinkProps>(
44
+ function AppProxyLink(props, ref) {
45
+ const context = useContext(AppProxyProviderContext);
46
+
47
+ if (!context) {
48
+ throw new Error(
49
+ 'AppProxyLink must be used within an AppProxyProvider component',
50
+ );
51
+ }
52
+
53
+ const {children, href, ...otherProps} = props;
54
+
55
+ return (
56
+ <a href={context.formatUrl(href)} {...otherProps} ref={ref}>
57
+ {children}
58
+ </a>
59
+ );
60
+ },
61
+ );
@@ -0,0 +1 @@
1
+ export {AppProxyLink, type AppProxyLinkProps} from './AppProxyLink';
@@ -0,0 +1,104 @@
1
+ import {createContext, useEffect, useState} from 'react';
2
+
3
+ /**
4
+ * Props for the `AppProxyProvider` component.
5
+ * @publicDocs
6
+ */
7
+ export interface AppProxyProviderProps {
8
+ /**
9
+ * The URL where the app is hosted. You can set this from the `SHOPIFY_APP_URL` environment variable.
10
+ */
11
+ appUrl: string;
12
+
13
+ /**
14
+ * The children to render.
15
+ */
16
+ children?: React.ReactNode;
17
+ }
18
+
19
+ type FormatUrlFunction = (
20
+ url: string | undefined,
21
+ addOrigin?: boolean,
22
+ ) => string | undefined;
23
+
24
+ interface AppProxyProviderContextProps {
25
+ appUrl: string;
26
+ formatUrl: FormatUrlFunction;
27
+ requestUrl?: URL;
28
+ }
29
+
30
+ export const AppProxyProviderContext =
31
+ createContext<AppProxyProviderContextProps | null>(null);
32
+
33
+ /**
34
+ * Sets up a page to render behind a Shopify app proxy, enabling JavaScript and CSS to be loaded from the app.
35
+ *
36
+ * Also provides components that enable using other components such as links and forms within proxies.
37
+ *
38
+ * > Caution:
39
+ * Because Remix doesn't support URL rewriting, any route using this component should <b>match the pathname of the proxy
40
+ * URL exactly</b>, and <b>end in a trailing slash</b> (e.g., `https://<shop>/apps/proxy/`).
41
+ *
42
+ * @example
43
+ * <caption>Wrap a route with an AppProxyProvider component.</caption>
44
+ * <description>Wrap your route component in the `AppProxyProvider` component and pass in your app URL.</description>
45
+ * ```ts
46
+ * // /app/routes/**\/*.ts
47
+ * import {authenticate} from '~/shopify.server';
48
+ * import {AppProxyProvider} from '@shopify/shopify-app-remix/react';
49
+ *
50
+ * export async function loader({ request }) {
51
+ * await authenticate.public.appProxy(request);
52
+ *
53
+ * return json({ appUrl: process.env.SHOPIFY_APP_URL });
54
+ * }
55
+ *
56
+ * export default function App() {
57
+ * const { appUrl } = useLoaderData();
58
+ *
59
+ * return (
60
+ * <AppProxyProvider appUrl={appUrl}>
61
+ * Page content
62
+ * </AppProxyProvider>
63
+ * );
64
+ * }
65
+ * ```
66
+ */
67
+ export function AppProxyProvider(props: AppProxyProviderProps) {
68
+ const {children, appUrl} = props;
69
+ const [requestUrl, setRequestUrl] = useState<URL | undefined>();
70
+
71
+ useEffect(
72
+ () => setRequestUrl(new URL(window.location.href)),
73
+ [setRequestUrl],
74
+ );
75
+
76
+ return (
77
+ <AppProxyProviderContext.Provider
78
+ value={{appUrl, requestUrl, formatUrl: formatProxyUrl(requestUrl)}}
79
+ >
80
+ <base href={appUrl} />
81
+
82
+ {children}
83
+ </AppProxyProviderContext.Provider>
84
+ );
85
+ }
86
+
87
+ function formatProxyUrl(requestUrl: URL | undefined): FormatUrlFunction {
88
+ return (url: string | undefined, addOrigin = true) => {
89
+ if (!url) {
90
+ return url;
91
+ }
92
+
93
+ let finalUrl = url;
94
+
95
+ if (addOrigin && requestUrl && finalUrl.startsWith('/')) {
96
+ finalUrl = new URL(`${requestUrl.origin}${url}`).href;
97
+ }
98
+ if (!finalUrl.endsWith('/')) {
99
+ finalUrl = `${finalUrl}/`;
100
+ }
101
+
102
+ return finalUrl;
103
+ };
104
+ }
@@ -0,0 +1,5 @@
1
+ export {
2
+ AppProxyProvider,
3
+ type AppProxyProviderProps,
4
+ AppProxyProviderContext,
5
+ } from './AppProxyProvider';
@@ -0,0 +1,14 @@
1
+ import React from 'react';
2
+ import {Link} from '@remix-run/react';
3
+ // Polaris' LinkProps doesn't match this type, so we need to import it directly
4
+ import type {
5
+ LinkLikeComponent,
6
+ LinkLikeComponentProps,
7
+ } from '@shopify/polaris/build/ts/src/utilities/link';
8
+
9
+ export const RemixPolarisLink = React.forwardRef<
10
+ HTMLAnchorElement,
11
+ LinkLikeComponentProps
12
+ >((props, ref) => (
13
+ <Link {...props} to={props.url ?? props.to} ref={ref} />
14
+ )) as LinkLikeComponent;
@@ -0,0 +1,4 @@
1
+ export * from './AppProvider';
2
+ export * from './AppProxyProvider';
3
+ export * from './AppProxyForm';
4
+ export * from './AppProxyLink';
@@ -0,0 +1,2 @@
1
+ export const APP_BRIDGE_URL =
2
+ 'https://cdn.shopify.com/shopifycloud/app-bridge.js';
@@ -0,0 +1 @@
1
+ export * from './components';
@@ -0,0 +1,14 @@
1
+ import {ApiVersion} from '@shopify/shopify-api';
2
+ import {TEST_SHOP} from '@shopify/shopify-api/test-helpers';
3
+
4
+ export {
5
+ API_KEY,
6
+ API_SECRET_KEY,
7
+ APP_URL,
8
+ TEST_SHOP_NAME,
9
+ TEST_SHOP,
10
+ SHOPIFY_HOST,
11
+ BASE64_HOST,
12
+ USER_ID,
13
+ } from '@shopify/shopify-api/test-helpers';
14
+ export const GRAPHQL_URL = `https://${TEST_SHOP}/admin/api/${ApiVersion.July25}/graphql.json`;
@@ -0,0 +1,51 @@
1
+ import {ApiVersion, Session} from '@shopify/shopify-api';
2
+
3
+ import type {AdminApiContext} from '../clients';
4
+
5
+ import {TEST_SHOP} from './const';
6
+ import {mockExternalRequest} from './request-mock';
7
+
8
+ export function expectAdminApiClient(
9
+ factory: () => Promise<{
10
+ admin: AdminApiContext;
11
+ expectedSession: Session;
12
+ actualSession: Session;
13
+ }>,
14
+ ) {
15
+ describe('Graphql client', () => {
16
+ it('can perform requests', async () => {
17
+ // GIVEN
18
+ const {admin, actualSession} = await factory();
19
+ await mockExternalRequest({
20
+ request: new Request(
21
+ `https://${TEST_SHOP}/admin/api/${ApiVersion.July25}/graphql.json`,
22
+ {
23
+ method: 'POST',
24
+ headers: {'X-Shopify-Access-Token': actualSession.accessToken!},
25
+ },
26
+ ),
27
+ response: new Response(
28
+ JSON.stringify({data: {shop: {name: 'Test shop'}}}),
29
+ ),
30
+ });
31
+
32
+ // WHEN
33
+ const response = await admin.graphql('{ shop { name } }');
34
+
35
+ // THEN
36
+ expect(response.status).toEqual(200);
37
+ expect(await response.json()).toEqual({
38
+ data: {shop: {name: 'Test shop'}},
39
+ headers: {'Content-Type': ['application/json']},
40
+ });
41
+ });
42
+
43
+ it('returns a session object as part of the context', async () => {
44
+ // GIVEN
45
+ const {expectedSession, actualSession} = await factory();
46
+
47
+ // THEN
48
+ expect(expectedSession).toEqual(actualSession);
49
+ });
50
+ });
51
+ }
@@ -0,0 +1,22 @@
1
+ import {TEST_SHOP} from './const';
2
+ import {testConfig} from './test-config';
3
+
4
+ export function expectBeginAuthRedirect(
5
+ config: ReturnType<typeof testConfig>,
6
+ response: Response,
7
+ ) {
8
+ expect(response.status).toEqual(302);
9
+
10
+ const {hostname, pathname, searchParams} = new URL(
11
+ response.headers.get('location')!,
12
+ );
13
+
14
+ expect(hostname).toBe(TEST_SHOP);
15
+ expect(pathname).toBe('/admin/oauth/authorize');
16
+ expect(searchParams.get('client_id')).toBe(config.apiKey);
17
+ expect(searchParams.get('scope')).toBe(config.scopes!.toString());
18
+ expect(searchParams.get('redirect_uri')).toBe(
19
+ `${config.appUrl}/auth/callback`,
20
+ );
21
+ expect(searchParams.get('state')).toStrictEqual(expect.any(String));
22
+ }
@@ -0,0 +1,25 @@
1
+ import {APP_BRIDGE_URL} from '../authenticate/const';
2
+
3
+ import {TEST_SHOP} from './const';
4
+
5
+ export function expectDocumentRequestHeaders(
6
+ response: Response,
7
+ isEmbeddedApp = true,
8
+ ) {
9
+ const headers = response.headers;
10
+
11
+ if (isEmbeddedApp) {
12
+ expect(headers.get('Content-Security-Policy')).toEqual(
13
+ `frame-ancestors https://${encodeURIComponent(
14
+ TEST_SHOP,
15
+ )} https://admin.shopify.com https://*.spin.dev https://admin.myshopify.io https://admin.shop.dev;`,
16
+ );
17
+ expect(headers.get('Link')).toEqual(
18
+ `<${APP_BRIDGE_URL}>; rel="preload"; as="script";`,
19
+ );
20
+ } else {
21
+ expect(headers.get('Content-Security-Policy')).toEqual(
22
+ `frame-ancestors 'none';`,
23
+ );
24
+ }
25
+ }
@@ -0,0 +1,37 @@
1
+ import {APP_URL, BASE64_HOST, TEST_SHOP} from './const';
2
+
3
+ interface ExpectExitIframeRedirectOptions {
4
+ shop?: string;
5
+ host?: string | null;
6
+ destination?: string;
7
+ addHostToExitIframePath?: boolean;
8
+ }
9
+
10
+ export function expectExitIframeRedirect(
11
+ response: Response,
12
+ {
13
+ shop = TEST_SHOP,
14
+ host = BASE64_HOST,
15
+ destination = `/auth?shop=${shop}`,
16
+ addHostToExitIframePath = true,
17
+ }: ExpectExitIframeRedirectOptions = {},
18
+ ) {
19
+ expect(response.status).toBe(302);
20
+
21
+ const {pathname, searchParams} = new URL(
22
+ response.headers.get('location')!,
23
+ APP_URL,
24
+ );
25
+ expect(pathname).toBe('/auth/exit-iframe');
26
+
27
+ expect(searchParams.get('shop')).toBe(shop);
28
+
29
+ let expectedDestination = destination;
30
+ if (host) {
31
+ expect(searchParams.get('host')).toBe(host);
32
+ if (addHostToExitIframePath) {
33
+ expectedDestination = destination.concat(`&host=${host}`);
34
+ }
35
+ }
36
+ expect(searchParams.get('exitIframe')).toBe(expectedDestination);
37
+ }
@@ -0,0 +1,8 @@
1
+ import {APP_URL} from './const';
2
+
3
+ export function expectLoginRedirect(response: Response) {
4
+ expect(response.status).toBe(302);
5
+
6
+ const {pathname} = new URL(response.headers.get('location')!, APP_URL);
7
+ expect(pathname).toBe('/auth/login');
8
+ }