@shopify/shopify-app-remix 4.2.0 → 5.0.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/AppProxyForm/AppProxyForm.js +4 -3
- package/dist/cjs/react/components/AppProxyForm/AppProxyForm.js.map +1 -1
- package/dist/cjs/react/components/AppProxyLink/AppProxyLink.js +4 -3
- package/dist/cjs/react/components/AppProxyLink/AppProxyLink.js.map +1 -1
- package/dist/cjs/react/components/AppProxyProvider/AppProxyProvider.js.map +1 -1
- package/dist/cjs/server/authenticate/admin/authenticate.js +1 -3
- package/dist/cjs/server/authenticate/admin/authenticate.js.map +1 -1
- package/dist/cjs/server/authenticate/admin/helpers/redirect.js +2 -2
- package/dist/cjs/server/authenticate/admin/helpers/redirect.js.map +1 -1
- package/dist/cjs/server/authenticate/admin/helpers/validate-redirect-url.js +1 -1
- package/dist/cjs/server/authenticate/admin/helpers/validate-redirect-url.js.map +1 -1
- package/dist/cjs/server/authenticate/public/appProxy/authenticate.js +9 -3
- package/dist/cjs/server/authenticate/public/appProxy/authenticate.js.map +1 -1
- package/dist/cjs/server/authenticate/webhooks/authenticate.js +1 -2
- package/dist/cjs/server/authenticate/webhooks/authenticate.js.map +1 -1
- package/dist/cjs/server/future/flags.js.map +1 -1
- package/dist/cjs/server/shopify-app.js +2 -1
- package/dist/cjs/server/shopify-app.js.map +1 -1
- package/dist/cjs/server/test-helpers/test-config.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/AppProxyForm/AppProxyForm.mjs +5 -4
- package/dist/esm/react/components/AppProxyForm/AppProxyForm.mjs.map +1 -1
- package/dist/esm/react/components/AppProxyLink/AppProxyLink.mjs +5 -4
- package/dist/esm/react/components/AppProxyLink/AppProxyLink.mjs.map +1 -1
- package/dist/esm/react/components/AppProxyProvider/AppProxyProvider.mjs.map +1 -1
- package/dist/esm/server/authenticate/admin/authenticate.mjs +1 -3
- package/dist/esm/server/authenticate/admin/authenticate.mjs.map +1 -1
- package/dist/esm/server/authenticate/admin/helpers/redirect.mjs +2 -2
- package/dist/esm/server/authenticate/admin/helpers/redirect.mjs.map +1 -1
- package/dist/esm/server/authenticate/admin/helpers/validate-redirect-url.mjs +1 -1
- package/dist/esm/server/authenticate/admin/helpers/validate-redirect-url.mjs.map +1 -1
- package/dist/esm/server/authenticate/public/appProxy/authenticate.mjs +9 -3
- package/dist/esm/server/authenticate/public/appProxy/authenticate.mjs.map +1 -1
- package/dist/esm/server/authenticate/webhooks/authenticate.mjs +1 -2
- package/dist/esm/server/authenticate/webhooks/authenticate.mjs.map +1 -1
- package/dist/esm/server/future/flags.mjs.map +1 -1
- package/dist/esm/server/shopify-app.mjs +2 -1
- package/dist/esm/server/shopify-app.mjs.map +1 -1
- package/dist/esm/server/test-helpers/test-config.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 +5 -1
- package/dist/ts/react/components/AppProvider/AppProvider.d.ts.map +1 -1
- package/dist/ts/react/components/AppProxyForm/AppProxyForm.d.ts +2 -1
- package/dist/ts/react/components/AppProxyForm/AppProxyForm.d.ts.map +1 -1
- package/dist/ts/react/components/AppProxyLink/AppProxyLink.d.ts +2 -1
- package/dist/ts/react/components/AppProxyLink/AppProxyLink.d.ts.map +1 -1
- package/dist/ts/react/components/AppProxyProvider/AppProxyProvider.d.ts +5 -1
- package/dist/ts/react/components/AppProxyProvider/AppProxyProvider.d.ts.map +1 -1
- package/dist/ts/server/authenticate/admin/authenticate.d.ts.map +1 -1
- package/dist/ts/server/authenticate/admin/billing/types.d.ts +4 -0
- package/dist/ts/server/authenticate/admin/billing/types.d.ts.map +1 -1
- package/dist/ts/server/authenticate/admin/scope/types.d.ts +2 -1
- package/dist/ts/server/authenticate/admin/scope/types.d.ts.map +1 -1
- package/dist/ts/server/authenticate/admin/types.d.ts +10 -0
- package/dist/ts/server/authenticate/admin/types.d.ts.map +1 -1
- package/dist/ts/server/authenticate/flow/types.d.ts +4 -0
- package/dist/ts/server/authenticate/flow/types.d.ts.map +1 -1
- package/dist/ts/server/authenticate/fulfillment-service/types.d.ts +4 -0
- package/dist/ts/server/authenticate/fulfillment-service/types.d.ts.map +1 -1
- package/dist/ts/server/authenticate/public/appProxy/types.d.ts +4 -0
- package/dist/ts/server/authenticate/public/appProxy/types.d.ts.map +1 -1
- package/dist/ts/server/authenticate/public/checkout/types.d.ts +4 -0
- package/dist/ts/server/authenticate/public/checkout/types.d.ts.map +1 -1
- package/dist/ts/server/authenticate/public/customer-account/types.d.ts +4 -0
- package/dist/ts/server/authenticate/public/customer-account/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 +6 -24
- package/dist/ts/server/authenticate/webhooks/types.d.ts.map +1 -1
- package/dist/ts/server/boundary/error.d.ts +1 -1
- package/dist/ts/server/boundary/error.d.ts.map +1 -1
- package/dist/ts/server/clients/admin/types.d.ts +4 -0
- package/dist/ts/server/clients/admin/types.d.ts.map +1 -1
- package/dist/ts/server/clients/storefront/types.d.ts +4 -0
- package/dist/ts/server/clients/storefront/types.d.ts.map +1 -1
- package/dist/ts/server/config-types.d.ts +1 -1
- package/dist/ts/server/future/flags.d.ts +6 -0
- package/dist/ts/server/future/flags.d.ts.map +1 -1
- package/dist/ts/server/shopify-app.d.ts +1 -0
- package/dist/ts/server/shopify-app.d.ts.map +1 -1
- package/dist/ts/server/test-helpers/test-config.d.ts.map +1 -1
- package/dist/ts/server/unauthenticated/admin/types.d.ts +4 -0
- package/dist/ts/server/unauthenticated/admin/types.d.ts.map +1 -1
- package/dist/ts/server/unauthenticated/storefront/types.d.ts +4 -0
- package/dist/ts/server/unauthenticated/storefront/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 +16 -17
- package/src/react/components/AppProvider/AppProvider.tsx +4 -0
- package/src/react/components/AppProxyForm/AppProxyForm.tsx +18 -15
- package/src/react/components/AppProxyLink/AppProxyLink.tsx +18 -15
- package/src/react/components/AppProxyProvider/AppProxyProvider.tsx +4 -0
- package/src/server/__test-helpers/const.ts +1 -1
- package/src/server/__test-helpers/expect-admin-api-client.ts +1 -1
- package/src/server/__test-helpers/expect-storefront-api-client.ts +1 -1
- package/src/server/__test-helpers/mock-graphql-request.ts +2 -2
- package/src/server/__test-helpers/request-mock.ts +1 -1
- package/src/server/__test-helpers/setup-non-embedded-flow.ts +1 -1
- package/src/server/__test-helpers/setup-valid-request.ts +1 -2
- package/src/server/__test-helpers/sign-request-cookie.ts +21 -10
- package/src/server/__test-helpers/test-config.ts +1 -1
- package/src/server/authenticate/admin/authenticate.ts +3 -6
- package/src/server/authenticate/admin/billing/types.ts +4 -0
- package/src/server/authenticate/admin/helpers/redirect.ts +2 -2
- package/src/server/authenticate/admin/helpers/validate-redirect-url.ts +1 -1
- package/src/server/authenticate/admin/scope/types.ts +2 -1
- package/src/server/authenticate/admin/types.ts +10 -0
- package/src/server/authenticate/flow/types.ts +4 -0
- package/src/server/authenticate/fulfillment-service/types.ts +4 -0
- package/src/server/authenticate/public/appProxy/authenticate.ts +9 -12
- package/src/server/authenticate/public/appProxy/types.ts +4 -0
- package/src/server/authenticate/public/checkout/types.ts +4 -0
- package/src/server/authenticate/public/customer-account/authenticate.public.customerAccount.doc.example.ts +2 -1
- package/src/server/authenticate/public/customer-account/types.ts +4 -0
- package/src/server/authenticate/webhooks/authenticate.ts +1 -2
- package/src/server/authenticate/webhooks/authenticate.webhooks.doc.example.ts +6 -6
- package/src/server/authenticate/webhooks/types.ts +7 -27
- package/src/server/clients/admin/types.ts +4 -0
- package/src/server/clients/storefront/types.ts +4 -0
- package/src/server/config-types.ts +1 -1
- package/src/server/future/flags.ts +6 -0
- package/src/server/shopify-app.ts +3 -4
- package/src/server/test-helpers/test-config.ts +3 -3
- package/src/server/unauthenticated/admin/types.ts +4 -0
- package/src/server/unauthenticated/storefront/types.ts +4 -0
- package/src/server/version.ts +1 -1
- package/dist/ts/react/components/AppProvider/AppProvider.doc.d.ts +0 -4
- package/dist/ts/react/components/AppProvider/AppProvider.doc.d.ts.map +0 -1
- package/dist/ts/react/components/AppProxyForm/AppProxyForm.doc.d.ts +0 -4
- package/dist/ts/react/components/AppProxyForm/AppProxyForm.doc.d.ts.map +0 -1
- package/dist/ts/react/components/AppProxyLink/AppProxyLink.doc.d.ts +0 -4
- package/dist/ts/react/components/AppProxyLink/AppProxyLink.doc.d.ts.map +0 -1
- package/dist/ts/react/components/AppProxyProvider/AppProxyProvider.doc.d.ts +0 -4
- package/dist/ts/react/components/AppProxyProvider/AppProxyProvider.doc.d.ts.map +0 -1
- package/dist/ts/server/authenticate/admin/authenticate.admin.doc.d.ts +0 -4
- package/dist/ts/server/authenticate/admin/authenticate.admin.doc.d.ts.map +0 -1
- package/dist/ts/server/authenticate/admin/billing/authenticate.admin.billing.doc.d.ts +0 -4
- package/dist/ts/server/authenticate/admin/billing/authenticate.admin.billing.doc.d.ts.map +0 -1
- package/dist/ts/server/authenticate/admin/scope/authenticate.admin.scopes.doc.d.ts +0 -4
- package/dist/ts/server/authenticate/admin/scope/authenticate.admin.scopes.doc.d.ts.map +0 -1
- package/dist/ts/server/authenticate/flow/authenticate.flow.doc.d.ts +0 -4
- package/dist/ts/server/authenticate/flow/authenticate.flow.doc.d.ts.map +0 -1
- package/dist/ts/server/authenticate/fulfillment-service/authenticate.fulfillment-service.doc.d.ts +0 -4
- package/dist/ts/server/authenticate/fulfillment-service/authenticate.fulfillment-service.doc.d.ts.map +0 -1
- package/dist/ts/server/authenticate/public/appProxy/authenticate.public.app-proxy.doc.d.ts +0 -4
- package/dist/ts/server/authenticate/public/appProxy/authenticate.public.app-proxy.doc.d.ts.map +0 -1
- package/dist/ts/server/authenticate/public/checkout/authenticate.public.checkout.doc.d.ts +0 -4
- package/dist/ts/server/authenticate/public/checkout/authenticate.public.checkout.doc.d.ts.map +0 -1
- package/dist/ts/server/authenticate/public/customer-account/authenticate.public.customerAccount.doc.d.ts +0 -4
- package/dist/ts/server/authenticate/public/customer-account/authenticate.public.customerAccount.doc.d.ts.map +0 -1
- package/dist/ts/server/authenticate/webhooks/authenticate.webhooks.doc.d.ts +0 -4
- package/dist/ts/server/authenticate/webhooks/authenticate.webhooks.doc.d.ts.map +0 -1
- package/dist/ts/server/clients/admin/authenticate.admin.api.doc.d.ts +0 -4
- package/dist/ts/server/clients/admin/authenticate.admin.api.doc.d.ts.map +0 -1
- package/dist/ts/server/clients/storefront/authenticate.storefront.api.doc.d.ts +0 -4
- package/dist/ts/server/clients/storefront/authenticate.storefront.api.doc.d.ts.map +0 -1
- package/dist/ts/server/shopify-app.doc.d.ts +0 -4
- package/dist/ts/server/shopify-app.doc.d.ts.map +0 -1
- package/dist/ts/server/unauthenticated/admin/unauthenticated.admin.doc.d.ts +0 -4
- package/dist/ts/server/unauthenticated/admin/unauthenticated.admin.doc.d.ts.map +0 -1
- package/dist/ts/server/unauthenticated/storefront/unauthenticated.storefront.doc.d.ts +0 -4
- package/dist/ts/server/unauthenticated/storefront/unauthenticated.storefront.doc.d.ts.map +0 -1
- package/src/react/components/AppProvider/AppProvider.doc.ts +0 -35
- package/src/react/components/AppProxyForm/AppProxyForm.doc.ts +0 -28
- package/src/react/components/AppProxyLink/AppProxyLink.doc.ts +0 -28
- package/src/react/components/AppProxyProvider/AppProxyProvider.doc.ts +0 -40
- package/src/server/authenticate/admin/authenticate.admin.doc.ts +0 -51
- package/src/server/authenticate/admin/billing/authenticate.admin.billing.doc.ts +0 -29
- package/src/server/authenticate/admin/scope/authenticate.admin.scopes.doc.ts +0 -28
- package/src/server/authenticate/flow/authenticate.flow.doc.ts +0 -47
- package/src/server/authenticate/fulfillment-service/authenticate.fulfillment-service.doc.ts +0 -48
- package/src/server/authenticate/public/appProxy/authenticate.public.app-proxy.doc.ts +0 -55
- package/src/server/authenticate/public/checkout/authenticate.public.checkout.doc.ts +0 -49
- package/src/server/authenticate/public/customer-account/authenticate.public.customerAccount.doc.ts +0 -49
- package/src/server/authenticate/webhooks/authenticate.webhooks.doc.ts +0 -39
- package/src/server/clients/admin/authenticate.admin.api.doc.ts +0 -34
- package/src/server/clients/storefront/authenticate.storefront.api.doc.ts +0 -34
- package/src/server/shopify-app.doc.ts +0 -45
- package/src/server/unauthenticated/admin/unauthenticated.admin.doc.ts +0 -40
- package/src/server/unauthenticated/storefront/unauthenticated.storefront.doc.ts +0 -40
package/package.json
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@shopify/shopify-app-remix",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "5.0.0",
|
|
4
4
|
"description": "Shopify Remix - to simplify the building of Shopify Apps with Remix",
|
|
5
5
|
"engines": {
|
|
6
|
-
"node": ">=
|
|
6
|
+
"node": ">=22.0.0"
|
|
7
7
|
},
|
|
8
8
|
"repository": {
|
|
9
9
|
"type": "git",
|
|
@@ -66,25 +66,25 @@
|
|
|
66
66
|
"Storefront API"
|
|
67
67
|
],
|
|
68
68
|
"devDependencies": {
|
|
69
|
-
"@babel/preset-env": "^7.
|
|
70
|
-
"@remix-run/node": "^2.17.
|
|
71
|
-
"@remix-run/react": "^2.17.
|
|
72
|
-
"@remix-run/testing": "^2.17.
|
|
73
|
-
"@shopify/generate-docs": "^
|
|
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.3",
|
|
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.
|
|
78
|
-
"jest-fetch-mock": "^3.0
|
|
76
|
+
"@shopify/shopify-app-session-storage-memory": "^7.0.0",
|
|
77
|
+
"@types/react": "^19.2.17",
|
|
78
|
+
"jest-fetch-mock": "^3.2.0"
|
|
79
79
|
},
|
|
80
80
|
"dependencies": {
|
|
81
|
-
"@remix-run/server-runtime": "^2.17.
|
|
82
|
-
"@shopify/admin-api-client": "^
|
|
83
|
-
"@shopify/shopify-api": "^
|
|
84
|
-
"@shopify/shopify-app-session-storage": "^
|
|
85
|
-
"@shopify/storefront-api-client": "^
|
|
81
|
+
"@remix-run/server-runtime": "^2.17.5",
|
|
82
|
+
"@shopify/admin-api-client": "^2.0.0",
|
|
83
|
+
"@shopify/shopify-api": "^14.0.0",
|
|
84
|
+
"@shopify/shopify-app-session-storage": "^6.0.0",
|
|
85
|
+
"@shopify/storefront-api-client": "^2.0.0",
|
|
86
86
|
"compare-versions": "^6.1.1",
|
|
87
|
-
"isbot": "^5.
|
|
87
|
+
"isbot": "^5.2.0"
|
|
88
88
|
},
|
|
89
89
|
"peerDependencies": {
|
|
90
90
|
"@remix-run/node": "*",
|
|
@@ -113,7 +113,6 @@
|
|
|
113
113
|
"scripts": {
|
|
114
114
|
"build-docs": "sh docs/build-docs.sh",
|
|
115
115
|
"validate-docs": "sh ./docs/build-docs.sh isTest && pnpm compare-docs",
|
|
116
|
-
"lint": "eslint .",
|
|
117
116
|
"build": "pnpm rollup && pnpm tsc -p ./tsconfig.build.json",
|
|
118
117
|
"tsc": "tsc",
|
|
119
118
|
"test": "jest",
|
|
@@ -12,6 +12,10 @@ import {useEffect} from 'react';
|
|
|
12
12
|
import {APP_BRIDGE_URL} from '../../const';
|
|
13
13
|
import {RemixPolarisLink} from '../RemixPolarisLink';
|
|
14
14
|
|
|
15
|
+
/**
|
|
16
|
+
* Props for the `AppProvider` component.
|
|
17
|
+
* @publicDocs
|
|
18
|
+
*/
|
|
15
19
|
export interface AppProviderProps extends Omit<
|
|
16
20
|
PolarisAppProviderProps,
|
|
17
21
|
'linkComponent' | 'i18n'
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {useContext} from 'react';
|
|
1
|
+
import {forwardRef, useContext} from 'react';
|
|
2
2
|
import {Form, type FormProps} from '@remix-run/react';
|
|
3
3
|
|
|
4
4
|
import {AppProxyProviderContext} from '../AppProxyProvider';
|
|
@@ -62,21 +62,24 @@ export interface AppProxyFormProps extends FormProps {
|
|
|
62
62
|
* );
|
|
63
63
|
* }
|
|
64
64
|
* ```
|
|
65
|
+
* @publicDocs
|
|
65
66
|
*/
|
|
66
|
-
export
|
|
67
|
-
|
|
67
|
+
export const AppProxyForm = forwardRef<HTMLFormElement, AppProxyFormProps>(
|
|
68
|
+
function AppProxyForm(props, ref) {
|
|
69
|
+
const context = useContext(AppProxyProviderContext);
|
|
68
70
|
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
71
|
+
if (!context) {
|
|
72
|
+
throw new Error(
|
|
73
|
+
'AppProxyForm must be used within an AppProxyProvider component',
|
|
74
|
+
);
|
|
75
|
+
}
|
|
74
76
|
|
|
75
|
-
|
|
77
|
+
const {children, action, ...otherProps} = props;
|
|
76
78
|
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
}
|
|
79
|
+
return (
|
|
80
|
+
<Form action={context.formatUrl(action, false)} {...otherProps} ref={ref}>
|
|
81
|
+
{children}
|
|
82
|
+
</Form>
|
|
83
|
+
);
|
|
84
|
+
},
|
|
85
|
+
);
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {useContext} from 'react';
|
|
1
|
+
import {forwardRef, useContext} from 'react';
|
|
2
2
|
|
|
3
3
|
import {AppProxyProviderContext} from '../AppProxyProvider';
|
|
4
4
|
|
|
@@ -38,21 +38,24 @@ export interface AppProxyLinkProps extends React.DetailedHTMLProps<
|
|
|
38
38
|
* );
|
|
39
39
|
* }
|
|
40
40
|
* ```
|
|
41
|
+
* @publicDocs
|
|
41
42
|
*/
|
|
42
|
-
export
|
|
43
|
-
|
|
43
|
+
export const AppProxyLink = forwardRef<HTMLAnchorElement, AppProxyLinkProps>(
|
|
44
|
+
function AppProxyLink(props, ref) {
|
|
45
|
+
const context = useContext(AppProxyProviderContext);
|
|
44
46
|
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
47
|
+
if (!context) {
|
|
48
|
+
throw new Error(
|
|
49
|
+
'AppProxyLink must be used within an AppProxyProvider component',
|
|
50
|
+
);
|
|
51
|
+
}
|
|
50
52
|
|
|
51
|
-
|
|
53
|
+
const {children, href, ...otherProps} = props;
|
|
52
54
|
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
}
|
|
55
|
+
return (
|
|
56
|
+
<a href={context.formatUrl(href)} {...otherProps} ref={ref}>
|
|
57
|
+
{children}
|
|
58
|
+
</a>
|
|
59
|
+
);
|
|
60
|
+
},
|
|
61
|
+
);
|
|
@@ -1,5 +1,9 @@
|
|
|
1
1
|
import {createContext, useEffect, useState} from 'react';
|
|
2
2
|
|
|
3
|
+
/**
|
|
4
|
+
* Props for the `AppProxyProvider` component.
|
|
5
|
+
* @publicDocs
|
|
6
|
+
*/
|
|
3
7
|
export interface AppProxyProviderProps {
|
|
4
8
|
/**
|
|
5
9
|
* The URL where the app is hosted. You can set this from the `SHOPIFY_APP_URL` environment variable.
|
|
@@ -11,4 +11,4 @@ export {
|
|
|
11
11
|
BASE64_HOST,
|
|
12
12
|
USER_ID,
|
|
13
13
|
} from '@shopify/shopify-api/test-helpers';
|
|
14
|
-
export const GRAPHQL_URL = `https://${TEST_SHOP}/admin/api/${ApiVersion.
|
|
14
|
+
export const GRAPHQL_URL = `https://${TEST_SHOP}/admin/api/${ApiVersion.July26}/graphql.json`;
|
|
@@ -18,7 +18,7 @@ export function expectAdminApiClient(
|
|
|
18
18
|
const {admin, actualSession} = await factory();
|
|
19
19
|
await mockExternalRequest({
|
|
20
20
|
request: new Request(
|
|
21
|
-
`https://${TEST_SHOP}/admin/api/${ApiVersion.
|
|
21
|
+
`https://${TEST_SHOP}/admin/api/${ApiVersion.July26}/graphql.json`,
|
|
22
22
|
{
|
|
23
23
|
method: 'POST',
|
|
24
24
|
headers: {'X-Shopify-Access-Token': actualSession.accessToken!},
|
|
@@ -18,7 +18,7 @@ export function expectStorefrontApiClient(
|
|
|
18
18
|
const apiResponse = {data: {blogs: {nodes: [{id: 1}]}}, headers: {}};
|
|
19
19
|
await mockExternalRequest({
|
|
20
20
|
request: new Request(
|
|
21
|
-
`https://${TEST_SHOP}/api/${ApiVersion.
|
|
21
|
+
`https://${TEST_SHOP}/api/${ApiVersion.July26}/graphql.json`,
|
|
22
22
|
{
|
|
23
23
|
method: 'POST',
|
|
24
24
|
headers: {
|
|
@@ -10,7 +10,7 @@ export interface MockGraphqlRequestArg {
|
|
|
10
10
|
}
|
|
11
11
|
|
|
12
12
|
export function mockGraphqlRequest(
|
|
13
|
-
apiVersion = ApiVersion.
|
|
13
|
+
apiVersion = ApiVersion.July26,
|
|
14
14
|
shopUrl = TEST_SHOP,
|
|
15
15
|
) {
|
|
16
16
|
return async function ({
|
|
@@ -32,7 +32,7 @@ export function mockGraphqlRequest(
|
|
|
32
32
|
}
|
|
33
33
|
|
|
34
34
|
export function mockGraphqlRequests(
|
|
35
|
-
apiVersion = ApiVersion.
|
|
35
|
+
apiVersion = ApiVersion.July26,
|
|
36
36
|
shopUrl = TEST_SHOP,
|
|
37
37
|
) {
|
|
38
38
|
return async function (...mocks: MockGraphqlRequestArg[]) {
|
|
@@ -99,7 +99,7 @@ export async function validateMocks() {
|
|
|
99
99
|
// TODO: we're currently not checking the headers properly. We should fix this.
|
|
100
100
|
Object.entries(request.headers).forEach(([key, value]) => {
|
|
101
101
|
expected.headers[key] = value;
|
|
102
|
-
actual.headers[key] = (init?.headers as any)[key];
|
|
102
|
+
actual.headers[key] = (init?.headers as any)?.[key];
|
|
103
103
|
});
|
|
104
104
|
}
|
|
105
105
|
|
|
@@ -14,7 +14,7 @@ export async function setUpNonEmbeddedFlow() {
|
|
|
14
14
|
const session = await setUpValidSession(shopify.sessionStorage);
|
|
15
15
|
|
|
16
16
|
const request = new Request(`${APP_URL}?shop=${TEST_SHOP}`);
|
|
17
|
-
signRequestCookie({
|
|
17
|
+
await signRequestCookie({
|
|
18
18
|
request,
|
|
19
19
|
cookieName: SESSION_COOKIE_NAME,
|
|
20
20
|
cookieValue: session.id,
|
|
@@ -20,8 +20,7 @@ interface ValidExtensionRequestOptions {
|
|
|
20
20
|
}
|
|
21
21
|
|
|
22
22
|
type ValidRequestOptions =
|
|
23
|
-
|
|
|
24
|
-
| ValidExtensionRequestOptions;
|
|
23
|
+
ValidBaseRequestOptions | ValidExtensionRequestOptions;
|
|
25
24
|
|
|
26
25
|
export async function setupValidRequest(
|
|
27
26
|
shopify: ShopifyApp<any>,
|
|
@@ -1,6 +1,12 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import {
|
|
2
|
+
Cookies,
|
|
3
|
+
NormalizedRequest,
|
|
4
|
+
NormalizedResponse,
|
|
5
|
+
} from '@shopify/shopify-api/runtime';
|
|
2
6
|
|
|
3
|
-
|
|
7
|
+
import {API_SECRET_KEY} from './const';
|
|
8
|
+
|
|
9
|
+
export async function signRequestCookie({
|
|
4
10
|
request,
|
|
5
11
|
cookieName,
|
|
6
12
|
cookieValue,
|
|
@@ -9,13 +15,18 @@ export function signRequestCookie({
|
|
|
9
15
|
cookieName: string;
|
|
10
16
|
cookieValue: string;
|
|
11
17
|
}) {
|
|
12
|
-
const
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
[
|
|
17
|
-
`${cookieName}=${cookieValue}`,
|
|
18
|
-
`${cookieName}.sig=${signedCookieValue}`,
|
|
19
|
-
].join(';'),
|
|
18
|
+
const cookies = new Cookies(
|
|
19
|
+
{headers: {}} as NormalizedRequest,
|
|
20
|
+
{} as NormalizedResponse,
|
|
21
|
+
{keys: [API_SECRET_KEY]},
|
|
20
22
|
);
|
|
23
|
+
|
|
24
|
+
await cookies.setAndSign(cookieName, cookieValue);
|
|
25
|
+
|
|
26
|
+
const cookieHeader = cookies
|
|
27
|
+
.toHeaders()
|
|
28
|
+
.map((cookie) => cookie.split(';')[0])
|
|
29
|
+
.join(';');
|
|
30
|
+
|
|
31
|
+
request.headers.set('Cookie', cookieHeader);
|
|
21
32
|
}
|
|
@@ -19,7 +19,7 @@ const TEST_FUTURE_FLAGS: Required<{[key in keyof FutureFlags]: true}> = {
|
|
|
19
19
|
|
|
20
20
|
// Override the helper's future flags and logger settings for our purposes
|
|
21
21
|
const TEST_CONFIG = {
|
|
22
|
-
apiVersion: ApiVersion.
|
|
22
|
+
apiVersion: ApiVersion.July26,
|
|
23
23
|
isEmbeddedApp: true,
|
|
24
24
|
future: TEST_FUTURE_FLAGS,
|
|
25
25
|
logger: {
|
|
@@ -80,8 +80,7 @@ export function authStrategyFactory<ConfigArg extends AppConfigArg>({
|
|
|
80
80
|
}
|
|
81
81
|
|
|
82
82
|
type AdminContextBase =
|
|
83
|
-
|
|
|
84
|
-
| NonEmbeddedAdminContext<ConfigArg>;
|
|
83
|
+
EmbeddedAdminContext<ConfigArg> | NonEmbeddedAdminContext<ConfigArg>;
|
|
85
84
|
|
|
86
85
|
function createContext(
|
|
87
86
|
request: Request,
|
|
@@ -157,13 +156,11 @@ export function authStrategyFactory<ConfigArg extends AppConfigArg>({
|
|
|
157
156
|
await ensureSessionTokenSearchParamIfRequired(params, request);
|
|
158
157
|
}
|
|
159
158
|
|
|
160
|
-
logger.info('Authenticating admin request', {
|
|
161
|
-
shop: getShopFromRequest(request),
|
|
162
|
-
});
|
|
163
|
-
|
|
164
159
|
const {payload, shop, sessionId, sessionToken} =
|
|
165
160
|
await getSessionTokenContext(params, request);
|
|
166
161
|
|
|
162
|
+
logger.info('Authenticating admin request', {shop});
|
|
163
|
+
|
|
167
164
|
logger.debug('Loading session from storage', {shop, sessionId});
|
|
168
165
|
const existingSession = sessionId
|
|
169
166
|
? await config.sessionStorage!.loadSession(sessionId)
|
|
@@ -118,6 +118,10 @@ export interface UpdateUsageCappedAmountOptions {
|
|
|
118
118
|
};
|
|
119
119
|
}
|
|
120
120
|
|
|
121
|
+
/**
|
|
122
|
+
* Provides utilities that apps can use to request billing for the app using the Admin API.
|
|
123
|
+
* @publicDocs
|
|
124
|
+
*/
|
|
121
125
|
export interface BillingContext<Config extends AppConfigArg> {
|
|
122
126
|
/**
|
|
123
127
|
* Checks if the shop has an active payment for any plan defined in the `billing` config option.
|
|
@@ -48,8 +48,8 @@ export function redirectFactory(
|
|
|
48
48
|
|
|
49
49
|
logger.debug('Redirecting', {shop, url: parsedUrl.toString()});
|
|
50
50
|
|
|
51
|
-
const isSameOrigin = parsedUrl.origin === config.appUrl;
|
|
52
|
-
if (isSameOrigin
|
|
51
|
+
const isSameOrigin = parsedUrl.origin === new URL(config.appUrl).origin;
|
|
52
|
+
if (isSameOrigin) {
|
|
53
53
|
searchParams.forEach((value, key) => {
|
|
54
54
|
if (!parsedUrl.searchParams.has(key)) {
|
|
55
55
|
parsedUrl.searchParams.set(key, value);
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
2
|
+
* Provides utilities that apps can use to [manage scopes](https://shopify.dev/docs/apps/build/authentication-authorization/app-installation/manage-access-scopes) for the app using the Admin API.
|
|
3
|
+
* @publicDocs
|
|
3
4
|
*/
|
|
4
5
|
export interface ScopesApiContext {
|
|
5
6
|
/**
|
|
@@ -148,6 +148,10 @@ export interface EmbeddedAdminContext<
|
|
|
148
148
|
*
|
|
149
149
|
* Returned only if `isEmbeddedApp` is `true`.
|
|
150
150
|
*
|
|
151
|
+
* Request parameters (including `id_token`, `shop`, `host`, and `embedded`) are only copied to the destination when
|
|
152
|
+
* it resolves to the same origin as `appUrl`. This helper does not validate the destination URL — avoid passing
|
|
153
|
+
* unvalidated user input (for example, a raw `?next=` query parameter) and use an allow-list of paths instead.
|
|
154
|
+
*
|
|
151
155
|
* @example
|
|
152
156
|
* <caption>Redirecting to an app route.</caption>
|
|
153
157
|
* <description>Use the `redirect` helper to safely redirect between pages.</description>
|
|
@@ -211,6 +215,12 @@ export interface ScopesContext {
|
|
|
211
215
|
export type AdminContext<Config extends AppConfigArg> =
|
|
212
216
|
EmbeddedTypedAdminContext<Config> & ScopesContext;
|
|
213
217
|
|
|
218
|
+
/**
|
|
219
|
+
* Authenticates requests coming from the Shopify admin.
|
|
220
|
+
*
|
|
221
|
+
* The shape of the returned object changes depending on the `isEmbeddedApp` config.
|
|
222
|
+
* @publicDocs
|
|
223
|
+
*/
|
|
214
224
|
export type AuthenticateAdmin<Config extends AppConfigArg> = (
|
|
215
225
|
request: Request,
|
|
216
226
|
) => Promise<AdminContext<Config>>;
|
|
@@ -77,6 +77,10 @@ export interface FulfillmentServiceContext {
|
|
|
77
77
|
payload: FulfillmentServicePayload;
|
|
78
78
|
}
|
|
79
79
|
|
|
80
|
+
/**
|
|
81
|
+
* Verifies requests coming from Shopify to fulfillment service apps.
|
|
82
|
+
* @publicDocs
|
|
83
|
+
*/
|
|
80
84
|
export type AuthenticateFulfillmentService = (
|
|
81
85
|
request: Request,
|
|
82
86
|
) => Promise<FulfillmentServiceContext>;
|
|
@@ -95,10 +95,9 @@ async function validateAppProxyHmac(
|
|
|
95
95
|
searchParams.delete('index');
|
|
96
96
|
}
|
|
97
97
|
|
|
98
|
-
let isValid = await api.utils.validateHmac(
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
);
|
|
98
|
+
let isValid = await api.utils.validateHmac(searchParams, {
|
|
99
|
+
signator: 'appProxy',
|
|
100
|
+
});
|
|
102
101
|
|
|
103
102
|
if (!isValid) {
|
|
104
103
|
const cleanPath = url.pathname
|
|
@@ -111,20 +110,18 @@ async function validateAppProxyHmac(
|
|
|
111
110
|
`?_data=${data}&${searchParams.toString().replace(/^\?/, '')}`,
|
|
112
111
|
);
|
|
113
112
|
|
|
114
|
-
isValid = await api.utils.validateHmac(
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
);
|
|
113
|
+
isValid = await api.utils.validateHmac(searchParams, {
|
|
114
|
+
signator: 'appProxy',
|
|
115
|
+
});
|
|
118
116
|
|
|
119
117
|
if (!isValid) {
|
|
120
118
|
const searchParams = new URLSearchParams(
|
|
121
119
|
`?_data=${data}._index&${url.search.replace(/^\?/, '')}`,
|
|
122
120
|
);
|
|
123
121
|
|
|
124
|
-
isValid = await api.utils.validateHmac(
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
);
|
|
122
|
+
isValid = await api.utils.validateHmac(searchParams, {
|
|
123
|
+
signator: 'appProxy',
|
|
124
|
+
});
|
|
128
125
|
}
|
|
129
126
|
}
|
|
130
127
|
|
|
@@ -2,6 +2,10 @@ import {Session} from '@shopify/shopify-api';
|
|
|
2
2
|
|
|
3
3
|
import {AdminApiContext, StorefrontContext} from '../../../clients';
|
|
4
4
|
|
|
5
|
+
/**
|
|
6
|
+
* Authenticates requests coming to the app from Shopify app proxies.
|
|
7
|
+
* @publicDocs
|
|
8
|
+
*/
|
|
5
9
|
export type AuthenticateAppProxy = (
|
|
6
10
|
request: Request,
|
|
7
11
|
) => Promise<AppProxyContext | AppProxyContextWithSession>;
|
|
@@ -2,6 +2,10 @@ import {JwtPayload} from '@shopify/shopify-api';
|
|
|
2
2
|
|
|
3
3
|
import {EnsureCORSFunction} from '../../helpers/ensure-cors-headers';
|
|
4
4
|
|
|
5
|
+
/**
|
|
6
|
+
* Authenticates requests coming from Shopify checkout extensions.
|
|
7
|
+
* @publicDocs
|
|
8
|
+
*/
|
|
5
9
|
export type AuthenticateCheckout = (
|
|
6
10
|
request: Request,
|
|
7
11
|
options?: AuthenticateCheckoutOptions,
|
|
@@ -10,7 +10,8 @@ export const loader = async ({request}: LoaderFunctionArgs) => {
|
|
|
10
10
|
};
|
|
11
11
|
|
|
12
12
|
export const action = async ({request}: ActionFunctionArgs) => {
|
|
13
|
-
const {cors, sessionToken} =
|
|
13
|
+
const {cors, sessionToken} =
|
|
14
|
+
await authenticate.public.customerAccount(request);
|
|
14
15
|
|
|
15
16
|
// Get offers for the customer
|
|
16
17
|
const offers = getOffers(sessionToken.des, sessionToken.sub);
|
|
@@ -2,6 +2,10 @@ import {JwtPayload} from '@shopify/shopify-api';
|
|
|
2
2
|
|
|
3
3
|
import {EnsureCORSFunction} from '../../helpers/ensure-cors-headers';
|
|
4
4
|
|
|
5
|
+
/**
|
|
6
|
+
* Authenticates requests coming from Shopify customer account extensions.
|
|
7
|
+
* @publicDocs
|
|
8
|
+
*/
|
|
5
9
|
export type AuthenticateCustomerAccount = (
|
|
6
10
|
request: Request,
|
|
7
11
|
options?: AuthenticateCustomerAccountOptions,
|
|
@@ -60,7 +60,6 @@ export function authenticateWebhookFactory<Topics extends string>(
|
|
|
60
60
|
topic: check.topic as Topics,
|
|
61
61
|
webhookId: check.webhookId,
|
|
62
62
|
payload: JSON.parse(rawBody),
|
|
63
|
-
subTopic: check.subTopic || undefined,
|
|
64
63
|
session: undefined,
|
|
65
64
|
admin: undefined,
|
|
66
65
|
webhookType: check.webhookType,
|
|
@@ -73,7 +72,7 @@ export function authenticateWebhookFactory<Topics extends string>(
|
|
|
73
72
|
apiVersion: check.apiVersion,
|
|
74
73
|
shop: check.domain,
|
|
75
74
|
topic: check.topic as Topics,
|
|
76
|
-
webhookId: check.
|
|
75
|
+
webhookId: check.webhookId,
|
|
77
76
|
payload: JSON.parse(rawBody),
|
|
78
77
|
session: undefined,
|
|
79
78
|
admin: undefined,
|
|
@@ -30,13 +30,13 @@ export const action = async ({request}: ActionFunctionArgs) => {
|
|
|
30
30
|
}
|
|
31
31
|
}
|
|
32
32
|
`,
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
33
|
+
{
|
|
34
|
+
variables: {
|
|
35
|
+
productId: payload.admin_graphql_api_id,
|
|
36
|
+
time: new Date().toISOString(),
|
|
37
|
+
},
|
|
37
38
|
},
|
|
38
|
-
|
|
39
|
-
);
|
|
39
|
+
);
|
|
40
40
|
}
|
|
41
41
|
|
|
42
42
|
return new Response();
|
|
@@ -75,11 +75,8 @@ interface Context<Topics = string | number | symbol> {
|
|
|
75
75
|
topic: Topics;
|
|
76
76
|
|
|
77
77
|
/**
|
|
78
|
-
* A unique ID for the webhook.
|
|
79
|
-
*
|
|
80
|
-
* For events webhooks (`webhookType === 'events'`), this is set to the `eventId` value for
|
|
81
|
-
* backwards compatibility. Prefer using `eventId` directly for events webhooks — `webhookId`
|
|
82
|
-
* will be removed from events webhooks in the next major version.
|
|
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.
|
|
83
80
|
*
|
|
84
81
|
* @example
|
|
85
82
|
* <caption>Webhook ID.</caption>
|
|
@@ -121,26 +118,6 @@ interface Context<Topics = string | number | symbol> {
|
|
|
121
118
|
*/
|
|
122
119
|
webhookType: WebhookTypeValue;
|
|
123
120
|
|
|
124
|
-
/**
|
|
125
|
-
* The sub-topic of the webhook. Only available for traditional webhooks.
|
|
126
|
-
*
|
|
127
|
-
* @example
|
|
128
|
-
* <caption>Webhook sub-topic.</caption>
|
|
129
|
-
* <description>Get the webhook sub-topic.</description>
|
|
130
|
-
* ```ts
|
|
131
|
-
* // /app/routes/webhooks.tsx
|
|
132
|
-
* import { ActionFunctionArgs } from "@remix-run/node";
|
|
133
|
-
* import { authenticate } from "../shopify.server";
|
|
134
|
-
*
|
|
135
|
-
* export const action = async ({ request }: ActionFunctionArgs) => {
|
|
136
|
-
* const { subTopic } = await authenticate.webhook(request);
|
|
137
|
-
* return new Response();
|
|
138
|
-
* };
|
|
139
|
-
* ```
|
|
140
|
-
*
|
|
141
|
-
*/
|
|
142
|
-
subTopic?: string;
|
|
143
|
-
|
|
144
121
|
/**
|
|
145
122
|
* The name assigned to the webhook subscription. Only available for traditional webhooks.
|
|
146
123
|
*/
|
|
@@ -258,9 +235,12 @@ export interface WebhookContextWithSession<
|
|
|
258
235
|
}
|
|
259
236
|
|
|
260
237
|
export type WebhookContext<Topics = string | number | symbol> =
|
|
261
|
-
|
|
|
262
|
-
| WebhookContextWithSession<Topics>;
|
|
238
|
+
WebhookContextWithoutSession<Topics> | WebhookContextWithSession<Topics>;
|
|
263
239
|
|
|
240
|
+
/**
|
|
241
|
+
* Verifies requests coming from Shopify webhooks.
|
|
242
|
+
* @publicDocs
|
|
243
|
+
*/
|
|
264
244
|
export type AuthenticateWebhook<Topics = string | number | symbol> = (
|
|
265
245
|
request: Request,
|
|
266
246
|
) => Promise<WebhookContext<Topics>>;
|
|
@@ -16,6 +16,10 @@ export type HandleAdminClientError = (arg: {
|
|
|
16
16
|
session: Session;
|
|
17
17
|
}) => Promise<void>;
|
|
18
18
|
|
|
19
|
+
/**
|
|
20
|
+
* Provides utilities that apps can use to make requests to the Admin API.
|
|
21
|
+
* @publicDocs
|
|
22
|
+
*/
|
|
19
23
|
export interface AdminApiContext {
|
|
20
24
|
/**
|
|
21
25
|
* Methods for interacting with the Shopify Admin GraphQL API
|
|
@@ -2,6 +2,10 @@ import {StorefrontOperations} from '@shopify/storefront-api-client';
|
|
|
2
2
|
|
|
3
3
|
import {GraphQLClient} from '../types';
|
|
4
4
|
|
|
5
|
+
/**
|
|
6
|
+
* Provides utilities that apps can use to make requests to the Storefront API.
|
|
7
|
+
* @publicDocs
|
|
8
|
+
*/
|
|
5
9
|
export interface StorefrontContext {
|
|
6
10
|
/**
|
|
7
11
|
* Method for interacting with the Shopify Storefront GraphQL API
|
|
@@ -4,6 +4,12 @@ import {AppConfig} from '../config-types';
|
|
|
4
4
|
|
|
5
5
|
// When adding new flags, you should also add them to the `TEST_FUTURE_FLAGS` object in `test-config.ts` to ensure that
|
|
6
6
|
// it doesn't cause regressions.
|
|
7
|
+
/**
|
|
8
|
+
* Set future flags using the `future` configuration field to opt in to upcoming breaking changes.
|
|
9
|
+
*
|
|
10
|
+
* With this feature, you can prepare for major releases ahead of time, as well as try out new features before they are released.
|
|
11
|
+
* @publicDocs
|
|
12
|
+
*/
|
|
7
13
|
export interface FutureFlags {
|
|
8
14
|
/**
|
|
9
15
|
* When enabled, embedded apps will fetch access tokens via [token exchange](https://shopify.dev/docs/apps/auth/get-access-tokens/token-exchange).
|