@shopify/shopify-app-remix 4.1.1 → 4.2.0
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.
- package/dist/cjs/react/components/AppProvider/AppProvider.js.map +1 -1
- package/dist/cjs/react/components/AppProxyLink/AppProxyLink.js.map +1 -1
- package/dist/cjs/server/authenticate/admin/strategies/auth-code-flow.js.map +1 -1
- package/dist/cjs/server/authenticate/admin/strategies/merchant-custom-app.js.map +1 -1
- package/dist/cjs/server/authenticate/admin/strategies/token-exchange.js.map +1 -1
- package/dist/cjs/server/authenticate/webhooks/authenticate.js +34 -10
- package/dist/cjs/server/authenticate/webhooks/authenticate.js.map +1 -1
- package/dist/cjs/server/version.js +1 -1
- package/dist/cjs/server/version.js.map +1 -1
- package/dist/esm/react/components/AppProvider/AppProvider.mjs.map +1 -1
- package/dist/esm/react/components/AppProxyLink/AppProxyLink.mjs.map +1 -1
- package/dist/esm/server/authenticate/admin/strategies/auth-code-flow.mjs.map +1 -1
- package/dist/esm/server/authenticate/admin/strategies/merchant-custom-app.mjs.map +1 -1
- package/dist/esm/server/authenticate/admin/strategies/token-exchange.mjs.map +1 -1
- package/dist/esm/server/authenticate/webhooks/authenticate.mjs +35 -11
- package/dist/esm/server/authenticate/webhooks/authenticate.mjs.map +1 -1
- package/dist/esm/server/version.mjs +1 -1
- package/dist/esm/server/version.mjs.map +1 -1
- package/dist/ts/react/components/AppProvider/AppProvider.d.ts.map +1 -1
- package/dist/ts/react/components/AppProxyLink/AppProxyLink.d.ts.map +1 -1
- package/dist/ts/server/authenticate/admin/billing/types.d.ts.map +1 -1
- package/dist/ts/server/authenticate/admin/strategies/auth-code-flow.d.ts.map +1 -1
- package/dist/ts/server/authenticate/admin/strategies/merchant-custom-app.d.ts.map +1 -1
- package/dist/ts/server/authenticate/admin/strategies/token-exchange.d.ts.map +1 -1
- package/dist/ts/server/authenticate/admin/types.d.ts.map +1 -1
- package/dist/ts/server/authenticate/public/extension/types.d.ts.map +1 -1
- package/dist/ts/server/authenticate/webhooks/authenticate.d.ts.map +1 -1
- package/dist/ts/server/authenticate/webhooks/types.d.ts +34 -2
- package/dist/ts/server/authenticate/webhooks/types.d.ts.map +1 -1
- package/dist/ts/server/config-types.d.ts.map +1 -1
- package/dist/ts/server/version.d.ts +1 -1
- package/dist/tsconfig.build.tsbuildinfo +1 -1
- package/package.json +18 -14
- package/src/react/components/AppProvider/AppProvider.doc.ts +35 -0
- package/src/react/components/AppProvider/AppProvider.tsx +147 -0
- package/src/react/components/AppProvider/index.ts +1 -0
- package/src/react/components/AppProxyForm/AppProxyForm.doc.ts +28 -0
- package/src/react/components/AppProxyForm/AppProxyForm.tsx +82 -0
- package/src/react/components/AppProxyForm/index.ts +1 -0
- package/src/react/components/AppProxyLink/AppProxyLink.doc.ts +28 -0
- package/src/react/components/AppProxyLink/AppProxyLink.tsx +58 -0
- package/src/react/components/AppProxyLink/index.ts +1 -0
- package/src/react/components/AppProxyProvider/AppProxyProvider.doc.ts +40 -0
- package/src/react/components/AppProxyProvider/AppProxyProvider.tsx +100 -0
- package/src/react/components/AppProxyProvider/index.ts +5 -0
- package/src/react/components/RemixPolarisLink.tsx +14 -0
- package/src/react/components/index.ts +4 -0
- package/src/react/const.ts +2 -0
- package/src/react/index.ts +1 -0
- package/src/server/__test-helpers/const.ts +14 -0
- package/src/server/__test-helpers/expect-admin-api-client.ts +51 -0
- package/src/server/__test-helpers/expect-begin-auth-redirect.ts +22 -0
- package/src/server/__test-helpers/expect-document-request-headers.ts +25 -0
- package/src/server/__test-helpers/expect-exit-iframe.ts +37 -0
- package/src/server/__test-helpers/expect-login-redirect.ts +8 -0
- package/src/server/__test-helpers/expect-storefront-api-client.ts +49 -0
- package/src/server/__test-helpers/expect-token-refresh.ts +188 -0
- package/src/server/__test-helpers/get-hmac.ts +7 -0
- package/src/server/__test-helpers/get-jwt.ts +10 -0
- package/src/server/__test-helpers/get-thrown-error.ts +19 -0
- package/src/server/__test-helpers/get-thrown-response.ts +17 -0
- package/src/server/__test-helpers/index.ts +21 -0
- package/src/server/__test-helpers/mock-graphql-request.ts +60 -0
- package/src/server/__test-helpers/request-mock.ts +169 -0
- package/src/server/__test-helpers/setup-embedded-flow.ts +30 -0
- package/src/server/__test-helpers/setup-fetch-flow.ts +29 -0
- package/src/server/__test-helpers/setup-non-embedded-flow.ts +31 -0
- package/src/server/__test-helpers/setup-valid-request.ts +78 -0
- package/src/server/__test-helpers/setup-valid-session.ts +28 -0
- package/src/server/__test-helpers/sign-request-cookie.ts +21 -0
- package/src/server/__test-helpers/test-config.ts +56 -0
- package/src/server/adapters/node/index.ts +13 -0
- package/src/server/adapters/vercel/index.ts +13 -0
- package/src/server/authenticate/admin/authenticate.admin.doc.example.ts +34 -0
- package/src/server/authenticate/admin/authenticate.admin.doc.ts +51 -0
- package/src/server/authenticate/admin/authenticate.ts +232 -0
- package/src/server/authenticate/admin/billing/authenticate.admin.billing.doc.ts +29 -0
- package/src/server/authenticate/admin/billing/cancel.ts +38 -0
- package/src/server/authenticate/admin/billing/check.ts +41 -0
- package/src/server/authenticate/admin/billing/create-usage-record.ts +36 -0
- package/src/server/authenticate/admin/billing/helpers.ts +42 -0
- package/src/server/authenticate/admin/billing/index.ts +6 -0
- package/src/server/authenticate/admin/billing/request.ts +64 -0
- package/src/server/authenticate/admin/billing/require.ts +58 -0
- package/src/server/authenticate/admin/billing/types.ts +613 -0
- package/src/server/authenticate/admin/billing/update-usage-subscription-capped-amount.ts +55 -0
- package/src/server/authenticate/admin/helpers/begin-auth.ts +17 -0
- package/src/server/authenticate/admin/helpers/create-admin-api-context.ts +20 -0
- package/src/server/authenticate/admin/helpers/ensure-app-is-embedded-if-required.ts +18 -0
- package/src/server/authenticate/admin/helpers/ensure-session-token-search-param-if-required.ts +25 -0
- package/src/server/authenticate/admin/helpers/handle-client-error.ts +45 -0
- package/src/server/authenticate/admin/helpers/index.ts +14 -0
- package/src/server/authenticate/admin/helpers/redirect-to-auth-page.ts +28 -0
- package/src/server/authenticate/admin/helpers/redirect-to-bounce-page.ts +23 -0
- package/src/server/authenticate/admin/helpers/redirect-to-install-page.ts +53 -0
- package/src/server/authenticate/admin/helpers/redirect-to-shopify-or-app-root.ts +21 -0
- package/src/server/authenticate/admin/helpers/redirect-with-app-bridge-headers.ts +13 -0
- package/src/server/authenticate/admin/helpers/redirect-with-exitiframe.ts +28 -0
- package/src/server/authenticate/admin/helpers/redirect.ts +164 -0
- package/src/server/authenticate/admin/helpers/render-app-bridge.ts +46 -0
- package/src/server/authenticate/admin/helpers/trigger-after-auth-hook.ts +29 -0
- package/src/server/authenticate/admin/helpers/validate-redirect-url.ts +67 -0
- package/src/server/authenticate/admin/helpers/validate-shop-and-host-params.ts +46 -0
- package/src/server/authenticate/admin/scope/authenticate.admin.scopes.doc.ts +28 -0
- package/src/server/authenticate/admin/scope/client/fetch-scopes-details.ts +43 -0
- package/src/server/authenticate/admin/scope/client/revoke-scopes.ts +38 -0
- package/src/server/authenticate/admin/scope/factory.ts +21 -0
- package/src/server/authenticate/admin/scope/query.ts +51 -0
- package/src/server/authenticate/admin/scope/request.ts +32 -0
- package/src/server/authenticate/admin/scope/revoke.ts +48 -0
- package/src/server/authenticate/admin/scope/types.ts +126 -0
- package/src/server/authenticate/admin/strategies/auth-code-flow.ts +332 -0
- package/src/server/authenticate/admin/strategies/merchant-custom-app.ts +65 -0
- package/src/server/authenticate/admin/strategies/token-exchange.ts +188 -0
- package/src/server/authenticate/admin/strategies/types.ts +29 -0
- package/src/server/authenticate/admin/types.ts +216 -0
- package/src/server/authenticate/const.ts +15 -0
- package/src/server/authenticate/flow/authenticate.flow.doc.example.ts +39 -0
- package/src/server/authenticate/flow/authenticate.flow.doc.ts +47 -0
- package/src/server/authenticate/flow/authenticate.ts +68 -0
- package/src/server/authenticate/flow/types.ts +85 -0
- package/src/server/authenticate/fulfillment-service/authenticate.fulfillment-service.doc.example.ts +52 -0
- package/src/server/authenticate/fulfillment-service/authenticate.fulfillment-service.doc.ts +48 -0
- package/src/server/authenticate/fulfillment-service/authenticate.ts +82 -0
- package/src/server/authenticate/fulfillment-service/types.ts +82 -0
- package/src/server/authenticate/helpers/add-response-headers.ts +43 -0
- package/src/server/authenticate/helpers/app-bridge-url.ts +10 -0
- package/src/server/authenticate/helpers/ensure-cors-headers.ts +38 -0
- package/src/server/authenticate/helpers/get-session-token-header.ts +11 -0
- package/src/server/authenticate/helpers/get-shop-from-request.ts +4 -0
- package/src/server/authenticate/helpers/idempotent-promise-handler.ts +45 -0
- package/src/server/authenticate/helpers/index.ts +10 -0
- package/src/server/authenticate/helpers/invalidate-access-token.ts +17 -0
- package/src/server/authenticate/helpers/reject-bot-request.ts +27 -0
- package/src/server/authenticate/helpers/respond-to-invalid-session-token.ts +28 -0
- package/src/server/authenticate/helpers/respond-to-options-request.ts +26 -0
- package/src/server/authenticate/helpers/validate-session-token.ts +40 -0
- package/src/server/authenticate/login/login.ts +53 -0
- package/src/server/authenticate/public/appProxy/authenticate.public.app-proxy.doc.example.ts +27 -0
- package/src/server/authenticate/public/appProxy/authenticate.public.app-proxy.doc.ts +55 -0
- package/src/server/authenticate/public/appProxy/authenticate.ts +150 -0
- package/src/server/authenticate/public/appProxy/types.ts +215 -0
- package/src/server/authenticate/public/checkout/authenticate.public.checkout.doc.example.offers.ts +21 -0
- package/src/server/authenticate/public/checkout/authenticate.public.checkout.doc.example.ts +17 -0
- package/src/server/authenticate/public/checkout/authenticate.public.checkout.doc.ts +49 -0
- package/src/server/authenticate/public/checkout/authenticate.ts +13 -0
- package/src/server/authenticate/public/checkout/types.ts +65 -0
- package/src/server/authenticate/public/customer-account/authenticate.public.customerAccount.doc.example.offers.ts +36 -0
- package/src/server/authenticate/public/customer-account/authenticate.public.customerAccount.doc.example.ts +18 -0
- package/src/server/authenticate/public/customer-account/authenticate.public.customerAccount.doc.ts +49 -0
- package/src/server/authenticate/public/customer-account/authenticate.ts +13 -0
- package/src/server/authenticate/public/customer-account/types.ts +65 -0
- package/src/server/authenticate/public/extension/authenticate.ts +54 -0
- package/src/server/authenticate/public/extension/types.ts +11 -0
- package/src/server/authenticate/public/factory.ts +24 -0
- package/src/server/authenticate/public/index.ts +1 -0
- package/src/server/authenticate/public/pos/authenticate.ts +8 -0
- package/src/server/authenticate/public/pos/types.ts +65 -0
- package/src/server/authenticate/public/types.ts +96 -0
- package/src/server/authenticate/webhooks/authenticate.ts +105 -0
- package/src/server/authenticate/webhooks/authenticate.webhooks.doc.example.ts +43 -0
- package/src/server/authenticate/webhooks/authenticate.webhooks.doc.ts +39 -0
- package/src/server/authenticate/webhooks/index.ts +1 -0
- package/src/server/authenticate/webhooks/register.ts +48 -0
- package/src/server/authenticate/webhooks/types.ts +266 -0
- package/src/server/boundary/error.tsx +14 -0
- package/src/server/boundary/headers.ts +15 -0
- package/src/server/boundary/index.ts +39 -0
- package/src/server/boundary/types.ts +4 -0
- package/src/server/clients/admin/authenticate.admin.api.doc.ts +34 -0
- package/src/server/clients/admin/factory.ts +22 -0
- package/src/server/clients/admin/graphql.ts +39 -0
- package/src/server/clients/admin/index.ts +2 -0
- package/src/server/clients/admin/types.ts +120 -0
- package/src/server/clients/index.ts +2 -0
- package/src/server/clients/storefront/authenticate.storefront.api.doc.ts +34 -0
- package/src/server/clients/storefront/factory.ts +32 -0
- package/src/server/clients/storefront/index.ts +2 -0
- package/src/server/clients/storefront/types.ts +78 -0
- package/src/server/clients/types.ts +47 -0
- package/src/server/config-types.ts +309 -0
- package/src/server/errors.ts +3 -0
- package/src/server/future/flags.ts +62 -0
- package/src/server/helpers/create-or-load-offline-session.ts +19 -0
- package/src/server/helpers/ensure-offline-token-is-not-expired.ts +32 -0
- package/src/server/helpers/ensure-valid-offline-session.ts +15 -0
- package/src/server/helpers/index.ts +4 -0
- package/src/server/helpers/refresh-token.ts +35 -0
- package/src/server/index.ts +24 -0
- package/src/server/override-logger.ts +42 -0
- package/src/server/shopify-app.doc.ts +45 -0
- package/src/server/shopify-app.ts +215 -0
- package/src/server/test-helpers/index.ts +1 -0
- package/src/server/test-helpers/test-config.ts +79 -0
- package/src/server/types-contexts.ts +44 -0
- package/src/server/types.ts +507 -0
- package/src/server/unauthenticated/admin/factory.ts +23 -0
- package/src/server/unauthenticated/admin/index.ts +1 -0
- package/src/server/unauthenticated/admin/types.ts +79 -0
- package/src/server/unauthenticated/admin/unauthenticated.admin.doc.ts +40 -0
- package/src/server/unauthenticated/storefront/factory.ts +28 -0
- package/src/server/unauthenticated/storefront/index.ts +1 -0
- package/src/server/unauthenticated/storefront/types.ts +105 -0
- package/src/server/unauthenticated/storefront/unauthenticated.storefront.doc.ts +40 -0
- package/src/server/unauthenticated/types.ts +65 -0
- package/src/server/version.ts +1 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@shopify/shopify-app-remix",
|
|
3
|
-
"version": "4.
|
|
3
|
+
"version": "4.2.0",
|
|
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.
|
|
70
|
-
"@remix-run/node": "^2.17.
|
|
71
|
-
"@remix-run/react": "^2.17.
|
|
72
|
-
"@remix-run/testing": "^2.17.
|
|
69
|
+
"@babel/preset-env": "^7.28.6",
|
|
70
|
+
"@remix-run/node": "^2.17.4",
|
|
71
|
+
"@remix-run/react": "^2.17.4",
|
|
72
|
+
"@remix-run/testing": "^2.17.4",
|
|
73
73
|
"@shopify/generate-docs": "^0.19.8",
|
|
74
74
|
"@shopify/polaris": "^12.18.0",
|
|
75
75
|
"@shopify/react-testing": "^6.0.0",
|
|
76
|
-
"@shopify/shopify-app-session-storage-memory": "^
|
|
77
|
-
"@types/react": "^19.2.
|
|
76
|
+
"@shopify/shopify-app-session-storage-memory": "^6.0.0",
|
|
77
|
+
"@types/react": "^19.2.9",
|
|
78
78
|
"jest-fetch-mock": "^3.0.3"
|
|
79
79
|
},
|
|
80
80
|
"dependencies": {
|
|
81
|
-
"@remix-run/server-runtime": "^2.17.
|
|
82
|
-
"@shopify/admin-api-client": "^1.1.
|
|
83
|
-
"@shopify/shopify-api": "^
|
|
84
|
-
"@shopify/shopify-app-session-storage": "^
|
|
85
|
-
"@shopify/storefront-api-client": "^1.0.
|
|
81
|
+
"@remix-run/server-runtime": "^2.17.4",
|
|
82
|
+
"@shopify/admin-api-client": "^1.1.2",
|
|
83
|
+
"@shopify/shopify-api": "^13.0.0",
|
|
84
|
+
"@shopify/shopify-app-session-storage": "^5.0.0",
|
|
85
|
+
"@shopify/storefront-api-client": "^1.0.10",
|
|
86
86
|
"compare-versions": "^6.1.1",
|
|
87
|
-
"isbot": "^5.1.
|
|
87
|
+
"isbot": "^5.1.34"
|
|
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 .
|
|
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,35 @@
|
|
|
1
|
+
import {ReferenceEntityTemplateSchema} from '@shopify/generate-docs';
|
|
2
|
+
|
|
3
|
+
const data: ReferenceEntityTemplateSchema = {
|
|
4
|
+
name: 'AppProvider',
|
|
5
|
+
descriptionType: 'AppProviderGeneratedType',
|
|
6
|
+
description: '',
|
|
7
|
+
category: 'Entrypoints',
|
|
8
|
+
type: 'component',
|
|
9
|
+
isVisualComponent: false,
|
|
10
|
+
definitions: [
|
|
11
|
+
{
|
|
12
|
+
title: 'AppProviderProps',
|
|
13
|
+
description: 'Props for the `AppProvider` component.',
|
|
14
|
+
type: 'AppProviderProps',
|
|
15
|
+
filePath: 'src/react/components/AppProvider/AppProvider.tsx',
|
|
16
|
+
},
|
|
17
|
+
],
|
|
18
|
+
jsDocTypeExamples: ['AppProviderGeneratedType'],
|
|
19
|
+
related: [
|
|
20
|
+
{
|
|
21
|
+
name: 'App bridge',
|
|
22
|
+
subtitle: 'Learn more about App Bridge.',
|
|
23
|
+
url: '/docs/api/app-bridge-library',
|
|
24
|
+
type: 'shopify',
|
|
25
|
+
},
|
|
26
|
+
{
|
|
27
|
+
name: 'Polaris',
|
|
28
|
+
subtitle: 'Learn more about Polaris.',
|
|
29
|
+
url: '/docs/apps/tools/polaris',
|
|
30
|
+
type: 'shopify',
|
|
31
|
+
},
|
|
32
|
+
],
|
|
33
|
+
};
|
|
34
|
+
|
|
35
|
+
export default data;
|
|
@@ -0,0 +1,147 @@
|
|
|
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
|
+
export interface AppProviderProps extends Omit<
|
|
16
|
+
PolarisAppProviderProps,
|
|
17
|
+
'linkComponent' | 'i18n'
|
|
18
|
+
> {
|
|
19
|
+
/**
|
|
20
|
+
* The API key for your Shopify app. This is the `Client ID` from the Partner Dashboard.
|
|
21
|
+
*
|
|
22
|
+
* When using the Shopify CLI, this is the `SHOPIFY_API_KEY` environment variable. If you're using the environment
|
|
23
|
+
* variable, then you need to pass it from the loader to the component.
|
|
24
|
+
*/
|
|
25
|
+
apiKey: string;
|
|
26
|
+
/**
|
|
27
|
+
* Whether the app is loaded inside the Shopify Admin. Default is `true`.
|
|
28
|
+
*
|
|
29
|
+
* {@link https://shopify.dev/docs/apps/admin/embedded-app-home}
|
|
30
|
+
*/
|
|
31
|
+
isEmbeddedApp?: boolean;
|
|
32
|
+
/**
|
|
33
|
+
* The internationalization (i18n) configuration for your Polaris provider.
|
|
34
|
+
*
|
|
35
|
+
* {@link https://polaris.shopify.com/components/utilities/app-provider}
|
|
36
|
+
*/
|
|
37
|
+
i18n?: PolarisAppProviderProps['i18n'];
|
|
38
|
+
/**
|
|
39
|
+
* Used internally by Shopify. You don't need to set this.
|
|
40
|
+
* @private
|
|
41
|
+
*/
|
|
42
|
+
__APP_BRIDGE_URL?: string;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* Sets up the Polaris `AppProvider` and injects the App Bridge script.
|
|
47
|
+
*
|
|
48
|
+
* This component extends the [`AppProvider`](https://polaris.shopify.com/components/utilities/app-provider) component
|
|
49
|
+
* from Polaris, and accepts all of its props except for `linkComponent`, which is overridden to use the Remix `Link`
|
|
50
|
+
* component.
|
|
51
|
+
*
|
|
52
|
+
* {@link https://polaris.shopify.com/components/utilities/app-provider}
|
|
53
|
+
* {@link https://shopify.dev/tools/app-bridge}
|
|
54
|
+
*
|
|
55
|
+
* @example
|
|
56
|
+
* <caption>Set up AppProvider.</caption>
|
|
57
|
+
* <description>Wrap your app in the `AppProvider` component and pass in your API key.</description>
|
|
58
|
+
* ```ts
|
|
59
|
+
* // /app/routes/**\/*.ts
|
|
60
|
+
* import {authenticate} from '~/shopify.server';
|
|
61
|
+
* import {AppProvider} from '@shopify/shopify-app-remix/react';
|
|
62
|
+
*
|
|
63
|
+
* export async function loader({ request }) {
|
|
64
|
+
* await authenticate.admin(request);
|
|
65
|
+
*
|
|
66
|
+
* return json({ apiKey: process.env.SHOPIFY_API_KEY });
|
|
67
|
+
* }
|
|
68
|
+
*
|
|
69
|
+
* export default function App() {
|
|
70
|
+
* const { apiKey } = useLoaderData();
|
|
71
|
+
*
|
|
72
|
+
* return (
|
|
73
|
+
* <AppProvider isEmbeddedApp apiKey={apiKey}>
|
|
74
|
+
* <Outlet />
|
|
75
|
+
* </AppProvider>
|
|
76
|
+
* );
|
|
77
|
+
* }
|
|
78
|
+
* ```
|
|
79
|
+
*
|
|
80
|
+
* @example
|
|
81
|
+
* <caption>Localize Polaris components.</caption>
|
|
82
|
+
* <description>Pass in a different locale for Polaris to translate its components.</description>
|
|
83
|
+
* ```ts
|
|
84
|
+
* // /app/routes/**\/*.ts
|
|
85
|
+
* import {authenticate} from '~/shopify.server';
|
|
86
|
+
* import {AppProvider} from '@shopify/shopify-app-remix/react';
|
|
87
|
+
*
|
|
88
|
+
* export async function loader({ request }) {
|
|
89
|
+
* await authenticate.admin(request);
|
|
90
|
+
*
|
|
91
|
+
* return json({
|
|
92
|
+
* apiKey: process.env.SHOPIFY_API_KEY,
|
|
93
|
+
* polarisTranslations: require("@shopify/polaris/locales/fr.json"),
|
|
94
|
+
* });
|
|
95
|
+
* }
|
|
96
|
+
*
|
|
97
|
+
* export default function App() {
|
|
98
|
+
* const { apiKey, polarisTranslations } = useLoaderData();
|
|
99
|
+
*
|
|
100
|
+
* return (
|
|
101
|
+
* <AppProvider apiKey={apiKey} i18n={polarisTranslations}>
|
|
102
|
+
* <Outlet />
|
|
103
|
+
* </AppProvider>
|
|
104
|
+
* );
|
|
105
|
+
* }
|
|
106
|
+
* ```
|
|
107
|
+
*/
|
|
108
|
+
export function AppProvider(props: AppProviderProps) {
|
|
109
|
+
const {
|
|
110
|
+
children,
|
|
111
|
+
apiKey,
|
|
112
|
+
i18n,
|
|
113
|
+
isEmbeddedApp = true,
|
|
114
|
+
__APP_BRIDGE_URL = APP_BRIDGE_URL,
|
|
115
|
+
...polarisProps
|
|
116
|
+
} = props;
|
|
117
|
+
|
|
118
|
+
const navigate = useNavigate();
|
|
119
|
+
|
|
120
|
+
useEffect(() => {
|
|
121
|
+
const handleNavigate = (event: Event) => {
|
|
122
|
+
const href = (event.target as HTMLElement)?.getAttribute('href');
|
|
123
|
+
if (href) {
|
|
124
|
+
navigate(href);
|
|
125
|
+
}
|
|
126
|
+
};
|
|
127
|
+
|
|
128
|
+
addEventListener('shopify:navigate', handleNavigate);
|
|
129
|
+
|
|
130
|
+
return () => {
|
|
131
|
+
removeEventListener('shopify:navigate', handleNavigate);
|
|
132
|
+
};
|
|
133
|
+
}, [navigate]);
|
|
134
|
+
|
|
135
|
+
return (
|
|
136
|
+
<>
|
|
137
|
+
{isEmbeddedApp && <script src={__APP_BRIDGE_URL} data-api-key={apiKey} />}
|
|
138
|
+
<PolarisAppProvider
|
|
139
|
+
{...polarisProps}
|
|
140
|
+
linkComponent={RemixPolarisLink}
|
|
141
|
+
i18n={i18n || englishI18n}
|
|
142
|
+
>
|
|
143
|
+
{children}
|
|
144
|
+
</PolarisAppProvider>
|
|
145
|
+
</>
|
|
146
|
+
);
|
|
147
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from './AppProvider';
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import {ReferenceEntityTemplateSchema} from '@shopify/generate-docs';
|
|
2
|
+
|
|
3
|
+
const data: ReferenceEntityTemplateSchema = {
|
|
4
|
+
name: 'AppProxyForm',
|
|
5
|
+
descriptionType: 'AppProxyFormGeneratedType',
|
|
6
|
+
description: '',
|
|
7
|
+
category: 'App proxy components',
|
|
8
|
+
type: 'component',
|
|
9
|
+
isVisualComponent: false,
|
|
10
|
+
definitions: [],
|
|
11
|
+
jsDocTypeExamples: ['AppProxyFormGeneratedType'],
|
|
12
|
+
related: [
|
|
13
|
+
{
|
|
14
|
+
name: 'authenticate.public.appProxy',
|
|
15
|
+
subtitle: 'Authenticating app proxy requests.',
|
|
16
|
+
url: '/docs/api/shopify-app-remix/authenticate/public/app-proxy',
|
|
17
|
+
type: 'remix',
|
|
18
|
+
},
|
|
19
|
+
{
|
|
20
|
+
name: 'AppProxyProvider',
|
|
21
|
+
subtitle: 'Enable JavaScript in pages loaded through app proxies.',
|
|
22
|
+
url: '/docs/api/shopify-app-remix/app-proxy-components/appproxyprovider',
|
|
23
|
+
type: 'remix',
|
|
24
|
+
},
|
|
25
|
+
],
|
|
26
|
+
};
|
|
27
|
+
|
|
28
|
+
export default data;
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
import {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
|
+
*/
|
|
66
|
+
export function AppProxyForm(props: AppProxyFormProps) {
|
|
67
|
+
const context = useContext(AppProxyProviderContext);
|
|
68
|
+
|
|
69
|
+
if (!context) {
|
|
70
|
+
throw new Error(
|
|
71
|
+
'AppProxyForm must be used within an AppProxyProvider component',
|
|
72
|
+
);
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
const {children, action, ...otherProps} = props;
|
|
76
|
+
|
|
77
|
+
return (
|
|
78
|
+
<Form action={context.formatUrl(action, false)} {...otherProps}>
|
|
79
|
+
{children}
|
|
80
|
+
</Form>
|
|
81
|
+
);
|
|
82
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {AppProxyForm, type AppProxyFormProps} from './AppProxyForm';
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import {ReferenceEntityTemplateSchema} from '@shopify/generate-docs';
|
|
2
|
+
|
|
3
|
+
const data: ReferenceEntityTemplateSchema = {
|
|
4
|
+
name: 'AppProxyLink',
|
|
5
|
+
descriptionType: 'AppProxyLinkGeneratedType',
|
|
6
|
+
description: '',
|
|
7
|
+
category: 'App proxy components',
|
|
8
|
+
type: 'component',
|
|
9
|
+
isVisualComponent: false,
|
|
10
|
+
definitions: [],
|
|
11
|
+
jsDocTypeExamples: ['AppProxyLinkGeneratedType'],
|
|
12
|
+
related: [
|
|
13
|
+
{
|
|
14
|
+
name: 'authenticate.public.appProxy',
|
|
15
|
+
subtitle: 'Authenticating app proxy requests.',
|
|
16
|
+
url: '/docs/api/shopify-app-remix/authenticate/public/app-proxy',
|
|
17
|
+
type: 'remix',
|
|
18
|
+
},
|
|
19
|
+
{
|
|
20
|
+
name: 'AppProxyProvider',
|
|
21
|
+
subtitle: 'Enable JavaScript in pages loaded through app proxies.',
|
|
22
|
+
url: '/docs/api/shopify-app-remix/app-proxy-components/appproxyprovider',
|
|
23
|
+
type: 'remix',
|
|
24
|
+
},
|
|
25
|
+
],
|
|
26
|
+
};
|
|
27
|
+
|
|
28
|
+
export default data;
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import {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
|
+
*/
|
|
42
|
+
export function AppProxyLink(props: AppProxyLinkProps) {
|
|
43
|
+
const context = useContext(AppProxyProviderContext);
|
|
44
|
+
|
|
45
|
+
if (!context) {
|
|
46
|
+
throw new Error(
|
|
47
|
+
'AppProxyLink must be used within an AppProxyProvider component',
|
|
48
|
+
);
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
const {children, href, ...otherProps} = props;
|
|
52
|
+
|
|
53
|
+
return (
|
|
54
|
+
<a href={context.formatUrl(href)} {...otherProps}>
|
|
55
|
+
{children}
|
|
56
|
+
</a>
|
|
57
|
+
);
|
|
58
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {AppProxyLink, type AppProxyLinkProps} from './AppProxyLink';
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import {ReferenceEntityTemplateSchema} from '@shopify/generate-docs';
|
|
2
|
+
|
|
3
|
+
const data: ReferenceEntityTemplateSchema = {
|
|
4
|
+
name: 'AppProxyProvider',
|
|
5
|
+
descriptionType: 'AppProxyProviderGeneratedType',
|
|
6
|
+
description: '',
|
|
7
|
+
category: 'Entrypoints',
|
|
8
|
+
type: 'component',
|
|
9
|
+
isVisualComponent: false,
|
|
10
|
+
definitions: [
|
|
11
|
+
{
|
|
12
|
+
title: 'AppProxyProviderProps',
|
|
13
|
+
description: 'Props for the `AppProxyProvider` component.',
|
|
14
|
+
type: 'AppProxyProviderProps',
|
|
15
|
+
},
|
|
16
|
+
],
|
|
17
|
+
jsDocTypeExamples: ['AppProxyProviderGeneratedType'],
|
|
18
|
+
related: [
|
|
19
|
+
{
|
|
20
|
+
name: 'authenticate.public.appProxy',
|
|
21
|
+
subtitle: 'Authenticate app proxy requests.',
|
|
22
|
+
url: '/docs/api/shopify-app-remix/authenticate/public/app-proxy',
|
|
23
|
+
type: 'remix',
|
|
24
|
+
},
|
|
25
|
+
{
|
|
26
|
+
name: 'AppProxyForm',
|
|
27
|
+
subtitle: 'Render form elements in proxies.',
|
|
28
|
+
url: '/docs/api/shopify-app-remix/app-proxy-components/appproxyform',
|
|
29
|
+
type: 'remix',
|
|
30
|
+
},
|
|
31
|
+
{
|
|
32
|
+
name: 'AppProxyLink',
|
|
33
|
+
subtitle: 'Render link elements in proxies.',
|
|
34
|
+
url: '/docs/api/shopify-app-remix/app-proxy-components/appproxylink',
|
|
35
|
+
type: 'remix',
|
|
36
|
+
},
|
|
37
|
+
],
|
|
38
|
+
};
|
|
39
|
+
|
|
40
|
+
export default data;
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
import {createContext, useEffect, useState} from 'react';
|
|
2
|
+
|
|
3
|
+
export interface AppProxyProviderProps {
|
|
4
|
+
/**
|
|
5
|
+
* The URL where the app is hosted. You can set this from the `SHOPIFY_APP_URL` environment variable.
|
|
6
|
+
*/
|
|
7
|
+
appUrl: string;
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* The children to render.
|
|
11
|
+
*/
|
|
12
|
+
children?: React.ReactNode;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
type FormatUrlFunction = (
|
|
16
|
+
url: string | undefined,
|
|
17
|
+
addOrigin?: boolean,
|
|
18
|
+
) => string | undefined;
|
|
19
|
+
|
|
20
|
+
interface AppProxyProviderContextProps {
|
|
21
|
+
appUrl: string;
|
|
22
|
+
formatUrl: FormatUrlFunction;
|
|
23
|
+
requestUrl?: URL;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
export const AppProxyProviderContext =
|
|
27
|
+
createContext<AppProxyProviderContextProps | null>(null);
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* Sets up a page to render behind a Shopify app proxy, enabling JavaScript and CSS to be loaded from the app.
|
|
31
|
+
*
|
|
32
|
+
* Also provides components that enable using other components such as links and forms within proxies.
|
|
33
|
+
*
|
|
34
|
+
* > Caution:
|
|
35
|
+
* Because Remix doesn't support URL rewriting, any route using this component should <b>match the pathname of the proxy
|
|
36
|
+
* URL exactly</b>, and <b>end in a trailing slash</b> (e.g., `https://<shop>/apps/proxy/`).
|
|
37
|
+
*
|
|
38
|
+
* @example
|
|
39
|
+
* <caption>Wrap a route with an AppProxyProvider component.</caption>
|
|
40
|
+
* <description>Wrap your route component in the `AppProxyProvider` component and pass in your app URL.</description>
|
|
41
|
+
* ```ts
|
|
42
|
+
* // /app/routes/**\/*.ts
|
|
43
|
+
* import {authenticate} from '~/shopify.server';
|
|
44
|
+
* import {AppProxyProvider} from '@shopify/shopify-app-remix/react';
|
|
45
|
+
*
|
|
46
|
+
* export async function loader({ request }) {
|
|
47
|
+
* await authenticate.public.appProxy(request);
|
|
48
|
+
*
|
|
49
|
+
* return json({ appUrl: process.env.SHOPIFY_APP_URL });
|
|
50
|
+
* }
|
|
51
|
+
*
|
|
52
|
+
* export default function App() {
|
|
53
|
+
* const { appUrl } = useLoaderData();
|
|
54
|
+
*
|
|
55
|
+
* return (
|
|
56
|
+
* <AppProxyProvider appUrl={appUrl}>
|
|
57
|
+
* Page content
|
|
58
|
+
* </AppProxyProvider>
|
|
59
|
+
* );
|
|
60
|
+
* }
|
|
61
|
+
* ```
|
|
62
|
+
*/
|
|
63
|
+
export function AppProxyProvider(props: AppProxyProviderProps) {
|
|
64
|
+
const {children, appUrl} = props;
|
|
65
|
+
const [requestUrl, setRequestUrl] = useState<URL | undefined>();
|
|
66
|
+
|
|
67
|
+
useEffect(
|
|
68
|
+
() => setRequestUrl(new URL(window.location.href)),
|
|
69
|
+
[setRequestUrl],
|
|
70
|
+
);
|
|
71
|
+
|
|
72
|
+
return (
|
|
73
|
+
<AppProxyProviderContext.Provider
|
|
74
|
+
value={{appUrl, requestUrl, formatUrl: formatProxyUrl(requestUrl)}}
|
|
75
|
+
>
|
|
76
|
+
<base href={appUrl} />
|
|
77
|
+
|
|
78
|
+
{children}
|
|
79
|
+
</AppProxyProviderContext.Provider>
|
|
80
|
+
);
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
function formatProxyUrl(requestUrl: URL | undefined): FormatUrlFunction {
|
|
84
|
+
return (url: string | undefined, addOrigin = true) => {
|
|
85
|
+
if (!url) {
|
|
86
|
+
return url;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
let finalUrl = url;
|
|
90
|
+
|
|
91
|
+
if (addOrigin && requestUrl && finalUrl.startsWith('/')) {
|
|
92
|
+
finalUrl = new URL(`${requestUrl.origin}${url}`).href;
|
|
93
|
+
}
|
|
94
|
+
if (!finalUrl.endsWith('/')) {
|
|
95
|
+
finalUrl = `${finalUrl}/`;
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
return finalUrl;
|
|
99
|
+
};
|
|
100
|
+
}
|
|
@@ -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 @@
|
|
|
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`;
|