@shopify/shopify-app-remix 4.1.0 → 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
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
import {StorefrontOperations} from '@shopify/storefront-api-client';
|
|
2
|
+
|
|
3
|
+
import {GraphQLClient} from '../types';
|
|
4
|
+
|
|
5
|
+
export interface StorefrontContext {
|
|
6
|
+
/**
|
|
7
|
+
* Method for interacting with the Shopify Storefront GraphQL API
|
|
8
|
+
*
|
|
9
|
+
* If you're getting incorrect type hints in the Shopify template, follow [these instructions](https://github.com/Shopify/shopify-app-template-remix/tree/main#incorrect-graphql-hints).
|
|
10
|
+
*
|
|
11
|
+
* {@link https://shopify.dev/docs/api/storefront}
|
|
12
|
+
*
|
|
13
|
+
* @example
|
|
14
|
+
* <caption>Querying the GraphQL API.</caption>
|
|
15
|
+
* <description>Use `storefront.graphql` to make query / mutation requests.</description>
|
|
16
|
+
* ```ts
|
|
17
|
+
* // app/routes/**\/.ts
|
|
18
|
+
* import { json } from "@remix-run/node";
|
|
19
|
+
* import { authenticate } from "../shopify.server";
|
|
20
|
+
*
|
|
21
|
+
* export async function action({ request }: ActionFunctionArgs) {
|
|
22
|
+
* const { storefront } = await authenticate.public.appProxy(request);
|
|
23
|
+
*
|
|
24
|
+
* const response = await storefront.graphql(`{blogs(first: 10) { edges { node { id } } } }`);
|
|
25
|
+
*
|
|
26
|
+
* return json(await response.json());
|
|
27
|
+
* }
|
|
28
|
+
* ```
|
|
29
|
+
*
|
|
30
|
+
* @example
|
|
31
|
+
* <caption>Handling GraphQL errors.</caption>
|
|
32
|
+
* <description>Catch `GraphqlQueryError` errors to see error messages from the API.</description>
|
|
33
|
+
* ```ts
|
|
34
|
+
* // /app/routes/**\/*.ts
|
|
35
|
+
* import { ActionFunctionArgs } from "@remix-run/node";
|
|
36
|
+
* import { authenticate } from "../shopify.server";
|
|
37
|
+
*
|
|
38
|
+
* export const action = async ({ request }: ActionFunctionArgs) => {
|
|
39
|
+
* const { storefront } = await authenticate.public.appProxy(request);
|
|
40
|
+
*
|
|
41
|
+
* try {
|
|
42
|
+
* const response = await storefront.graphql(
|
|
43
|
+
* `#graphql
|
|
44
|
+
* query incorrectQuery {
|
|
45
|
+
* products(first: 10) {
|
|
46
|
+
* nodes {
|
|
47
|
+
* not_a_field
|
|
48
|
+
* }
|
|
49
|
+
* }
|
|
50
|
+
* }`,
|
|
51
|
+
* );
|
|
52
|
+
*
|
|
53
|
+
* return json({ data: await response.json() });
|
|
54
|
+
* } catch (error) {
|
|
55
|
+
* if (error instanceof GraphqlQueryError) {
|
|
56
|
+
* // { errors: { graphQLErrors: [
|
|
57
|
+
* // { message: "Field 'not_a_field' doesn't exist on type 'Product'" }
|
|
58
|
+
* // ] } }
|
|
59
|
+
* return json({ errors: error.body?.errors }, { status: 500 });
|
|
60
|
+
* }
|
|
61
|
+
* return json({ message: "An error occurred" }, { status: 500 });
|
|
62
|
+
* }
|
|
63
|
+
* }
|
|
64
|
+
* ```
|
|
65
|
+
*
|
|
66
|
+
* ```ts
|
|
67
|
+
* // /app/shopify.server.ts
|
|
68
|
+
* import { shopifyApp } from "@shopify/shopify-app-remix/server";
|
|
69
|
+
*
|
|
70
|
+
* const shopify = shopifyApp({
|
|
71
|
+
* // ...
|
|
72
|
+
* });
|
|
73
|
+
* export default shopify;
|
|
74
|
+
* export const authenticate = shopify.authenticate;
|
|
75
|
+
* ```
|
|
76
|
+
*/
|
|
77
|
+
graphql: GraphQLClient<StorefrontOperations>;
|
|
78
|
+
}
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import type {
|
|
2
|
+
AllOperations,
|
|
3
|
+
ReturnData,
|
|
4
|
+
FetchResponseBody,
|
|
5
|
+
ApiClientRequestOptions,
|
|
6
|
+
ResponseWithType,
|
|
7
|
+
} from '@shopify/admin-api-client';
|
|
8
|
+
import {ApiVersion} from '@shopify/shopify-api';
|
|
9
|
+
|
|
10
|
+
export interface GraphQLQueryOptions<
|
|
11
|
+
Operation extends keyof Operations,
|
|
12
|
+
Operations extends AllOperations,
|
|
13
|
+
> {
|
|
14
|
+
/**
|
|
15
|
+
* The variables to pass to the operation.
|
|
16
|
+
*/
|
|
17
|
+
variables?: ApiClientRequestOptions<Operation, Operations>['variables'];
|
|
18
|
+
/**
|
|
19
|
+
* The version of the API to use for the request.
|
|
20
|
+
*/
|
|
21
|
+
apiVersion?: ApiVersion;
|
|
22
|
+
/**
|
|
23
|
+
* Additional headers to include in the request.
|
|
24
|
+
*/
|
|
25
|
+
headers?: Record<string, any>;
|
|
26
|
+
/**
|
|
27
|
+
* The total number of times to try the request if it fails.
|
|
28
|
+
*/
|
|
29
|
+
tries?: number;
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* An optional AbortSignal to cancel the request.
|
|
33
|
+
*/
|
|
34
|
+
signal?: AbortSignal;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
export type GraphQLResponse<
|
|
38
|
+
Operation extends keyof Operations,
|
|
39
|
+
Operations extends AllOperations,
|
|
40
|
+
> = ResponseWithType<FetchResponseBody<ReturnData<Operation, Operations>>>;
|
|
41
|
+
|
|
42
|
+
export type GraphQLClient<Operations extends AllOperations> = <
|
|
43
|
+
Operation extends keyof Operations,
|
|
44
|
+
>(
|
|
45
|
+
query: Operation,
|
|
46
|
+
options?: GraphQLQueryOptions<Operation, Operations>,
|
|
47
|
+
) => Promise<GraphQLResponse<Operation, Operations>>;
|
|
@@ -0,0 +1,309 @@
|
|
|
1
|
+
import {
|
|
2
|
+
ConfigParams as ApiConfigArg,
|
|
3
|
+
ConfigInterface as ApiConfig,
|
|
4
|
+
Session,
|
|
5
|
+
ApiVersion,
|
|
6
|
+
WebhookHandler,
|
|
7
|
+
} from '@shopify/shopify-api';
|
|
8
|
+
import {SessionStorage} from '@shopify/shopify-app-session-storage';
|
|
9
|
+
|
|
10
|
+
import type {
|
|
11
|
+
ApiFutureFlags,
|
|
12
|
+
FutureFlagOptions,
|
|
13
|
+
FutureFlags,
|
|
14
|
+
} from './future/flags';
|
|
15
|
+
import type {AppDistribution} from './types';
|
|
16
|
+
import type {AdminApiContext} from './clients';
|
|
17
|
+
import {IdempotentPromiseHandler} from './authenticate/helpers/idempotent-promise-handler';
|
|
18
|
+
|
|
19
|
+
export interface AppConfigArg<
|
|
20
|
+
Storage extends SessionStorage = SessionStorage,
|
|
21
|
+
Future extends FutureFlagOptions = FutureFlagOptions,
|
|
22
|
+
> extends Omit<
|
|
23
|
+
ApiConfigArg<ApiFutureFlags<Future>>,
|
|
24
|
+
| 'hostName'
|
|
25
|
+
| 'hostScheme'
|
|
26
|
+
| 'isEmbeddedApp'
|
|
27
|
+
| 'apiVersion'
|
|
28
|
+
| 'isCustomStoreApp'
|
|
29
|
+
| 'future'
|
|
30
|
+
> {
|
|
31
|
+
/**
|
|
32
|
+
* The URL your app is running on.
|
|
33
|
+
*
|
|
34
|
+
* The `@shopify/cli` provides this URL as `process.env.SHOPIFY_APP_URL`. For development this is probably a tunnel URL that points to your local machine. If this is a production app, this is your production URL.
|
|
35
|
+
*/
|
|
36
|
+
appUrl: string;
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* An adaptor for storing sessions in your database of choice.
|
|
40
|
+
*
|
|
41
|
+
* Shopify provides multiple session storage adaptors and you can create your own.
|
|
42
|
+
*
|
|
43
|
+
* Optional for apps created in the Shopify Admin.
|
|
44
|
+
*
|
|
45
|
+
* {@link https://github.com/Shopify/shopify-app-js/blob/main/README.md#session-storage-options}
|
|
46
|
+
*
|
|
47
|
+
* @example
|
|
48
|
+
* <caption>Storing sessions with Prisma.</caption>
|
|
49
|
+
* <description>Add the `@shopify/shopify-app-session-storage-prisma` package to use the Prisma session storage.</description>
|
|
50
|
+
* ```ts
|
|
51
|
+
* import { shopifyApp } from "@shopify/shopify-app-remix/server";
|
|
52
|
+
* import { PrismaSessionStorage } from "@shopify/shopify-app-session-storage-prisma";
|
|
53
|
+
*
|
|
54
|
+
* import prisma from "~/db.server";
|
|
55
|
+
*
|
|
56
|
+
* const shopify = shopifyApp({
|
|
57
|
+
* // ... etc
|
|
58
|
+
* sessionStorage: new PrismaSessionStorage(prisma),
|
|
59
|
+
* });
|
|
60
|
+
* export default shopify;
|
|
61
|
+
* ```
|
|
62
|
+
*/
|
|
63
|
+
sessionStorage?: Storage;
|
|
64
|
+
|
|
65
|
+
/**
|
|
66
|
+
* Whether your app use online or offline tokens.
|
|
67
|
+
*
|
|
68
|
+
* If your app uses online tokens, then both online and offline tokens will be saved to your database. This ensures your app can perform background jobs.
|
|
69
|
+
*
|
|
70
|
+
* {@link https://shopify.dev/docs/apps/auth/oauth/access-modes}
|
|
71
|
+
*
|
|
72
|
+
* @defaultValue `false`
|
|
73
|
+
*/
|
|
74
|
+
useOnlineTokens?: boolean;
|
|
75
|
+
|
|
76
|
+
/**
|
|
77
|
+
* The config for the shop-specific webhooks your app needs.
|
|
78
|
+
*
|
|
79
|
+
* Use this to configure shop-specific webhooks. In many cases defining app-specific webhooks in the `shopify.app.toml` will be sufficient and easier to manage. Please see:
|
|
80
|
+
*
|
|
81
|
+
* {@link https://shopify.dev/docs/apps/build/webhooks/subscribe#app-specific-vs-shop-specific-subscriptions}
|
|
82
|
+
*
|
|
83
|
+
* You should only use this if you need shop-specific webhooks. If you do need shop-specific webhooks this can be in used in conjunction with the afterAuth hook, loaders or processes such as background jobs.
|
|
84
|
+
*
|
|
85
|
+
* @example
|
|
86
|
+
* <caption>Registering shop-specific webhooks.</caption>
|
|
87
|
+
* ```ts
|
|
88
|
+
* // app/shopify.server.ts
|
|
89
|
+
* import { DeliveryMethod, shopifyApp } from "@shopify/shopify-app-remix/server";
|
|
90
|
+
*
|
|
91
|
+
* const shopify = shopifyApp({
|
|
92
|
+
* webhooks: {
|
|
93
|
+
* PRODUCTS_CREATE: {
|
|
94
|
+
* deliveryMethod: DeliveryMethod.Http,
|
|
95
|
+
* callbackUrl: "/webhooks/products/create",
|
|
96
|
+
* },
|
|
97
|
+
* },
|
|
98
|
+
* hooks: {
|
|
99
|
+
* afterAuth: async ({ session }) => {
|
|
100
|
+
* // Register webhooks for the shop
|
|
101
|
+
* // In this example, every shop will have these webhooks
|
|
102
|
+
* // But you could wrap this in some custom shop specific conditional logic
|
|
103
|
+
* shopify.registerWebhooks({ session });
|
|
104
|
+
* }
|
|
105
|
+
* },
|
|
106
|
+
* // ...etc
|
|
107
|
+
* });
|
|
108
|
+
* export default shopify;
|
|
109
|
+
* export const authenticate = shopify.authenticate;
|
|
110
|
+
* ```
|
|
111
|
+
*
|
|
112
|
+
* @example
|
|
113
|
+
* <caption>Registering app-specific webhooks (Recommended)</caption>
|
|
114
|
+
* ```toml
|
|
115
|
+
* # shopify.app.toml
|
|
116
|
+
* [webhooks]
|
|
117
|
+
* api_version = "2024-07"
|
|
118
|
+
|
|
119
|
+
* [[webhooks.subscriptions]]
|
|
120
|
+
* topics = ["products/create"]
|
|
121
|
+
* uri = "/webhooks/products/create"
|
|
122
|
+
*
|
|
123
|
+
* ```
|
|
124
|
+
*
|
|
125
|
+
* @example
|
|
126
|
+
* <caption>Authenticating a webhook request</caption>
|
|
127
|
+
*
|
|
128
|
+
* ```ts
|
|
129
|
+
* // /app/routes/webhooks.ts
|
|
130
|
+
* import { ActionFunctionArgs } from "@remix-run/node";
|
|
131
|
+
* import { authenticate } from "../shopify.server";
|
|
132
|
+
* import db from "../db.server";
|
|
133
|
+
*
|
|
134
|
+
* export const action = async ({ request }: ActionFunctionArgs) => {
|
|
135
|
+
* const { topic, shop, session, payload } = await authenticate.webhook(request);
|
|
136
|
+
*
|
|
137
|
+
* // Webhook requests can trigger after an app is uninstalled
|
|
138
|
+
* // If the app is already uninstalled, the session may be undefined.
|
|
139
|
+
* if (!session) {
|
|
140
|
+
* throw new Response();
|
|
141
|
+
* }
|
|
142
|
+
*
|
|
143
|
+
* // Handle the webhook
|
|
144
|
+
* console.log(`${TOPIC} webhook received with`, JSON.stringify(payload))
|
|
145
|
+
*
|
|
146
|
+
* throw new Response();
|
|
147
|
+
* };
|
|
148
|
+
* ```
|
|
149
|
+
*/
|
|
150
|
+
webhooks?: WebhookConfig;
|
|
151
|
+
|
|
152
|
+
/**
|
|
153
|
+
* Functions to call at key places during your apps lifecycle.
|
|
154
|
+
*
|
|
155
|
+
* These functions are called in the context of the request that triggered them. This means you can access the session.
|
|
156
|
+
*
|
|
157
|
+
* @example
|
|
158
|
+
* <caption>Seeding your database custom data when a merchant installs your app.</caption>
|
|
159
|
+
* ```ts
|
|
160
|
+
* import { DeliveryMethod, shopifyApp } from "@shopify/shopify-app-remix/server";
|
|
161
|
+
* import { seedStoreData } from "~/db/seeds"
|
|
162
|
+
*
|
|
163
|
+
* const shopify = shopifyApp({
|
|
164
|
+
* hooks: {
|
|
165
|
+
* afterAuth: async ({ session }) => {
|
|
166
|
+
* seedStoreData({session})
|
|
167
|
+
* }
|
|
168
|
+
* },
|
|
169
|
+
* // ...etc
|
|
170
|
+
* });
|
|
171
|
+
* ```
|
|
172
|
+
*/
|
|
173
|
+
hooks?: HooksConfig;
|
|
174
|
+
|
|
175
|
+
/**
|
|
176
|
+
* Does your app render embedded inside the Shopify Admin or on its own.
|
|
177
|
+
*
|
|
178
|
+
* Unless you have very specific needs, this should be true.
|
|
179
|
+
*
|
|
180
|
+
* @defaultValue `true`
|
|
181
|
+
*/
|
|
182
|
+
isEmbeddedApp?: boolean;
|
|
183
|
+
|
|
184
|
+
/**
|
|
185
|
+
* How your app is distributed. Default is `AppDistribution.AppStore`.
|
|
186
|
+
*
|
|
187
|
+
* AppStore should be used for public apps that are distributed in the Shopify App Store.
|
|
188
|
+
* SingleMerchant should be used for custom apps managed in the Partner Dashboard.
|
|
189
|
+
* ShopifyAdmin should be used for apps that are managed in the merchant's Shopify Admin.
|
|
190
|
+
*
|
|
191
|
+
* {@link https://shopify.dev/docs/apps/distribution}
|
|
192
|
+
*/
|
|
193
|
+
distribution?: AppDistribution;
|
|
194
|
+
|
|
195
|
+
/**
|
|
196
|
+
* What version of Shopify's Admin API's would you like to use.
|
|
197
|
+
*
|
|
198
|
+
* {@link https://shopify.dev/docs/api/}
|
|
199
|
+
*
|
|
200
|
+
* @example
|
|
201
|
+
* <caption>Using a specific API Version</caption>
|
|
202
|
+
* ```ts
|
|
203
|
+
* import { ApiVersion, shopifyApp } from "@shopify/shopify-app-remix/server";
|
|
204
|
+
*
|
|
205
|
+
* const shopify = shopifyApp({
|
|
206
|
+
* // ...etc
|
|
207
|
+
* apiVersion: ApiVersion.July25,
|
|
208
|
+
* });
|
|
209
|
+
* ```
|
|
210
|
+
*/
|
|
211
|
+
apiVersion: ApiVersion;
|
|
212
|
+
|
|
213
|
+
/**
|
|
214
|
+
* A path that Shopify can reserve for auth related endpoints.
|
|
215
|
+
*
|
|
216
|
+
* This must match a $ route in your Remix app. That route must export a loader function that calls `shopify.authenticate.admin(request)`.
|
|
217
|
+
*
|
|
218
|
+
* @default `"/auth"`
|
|
219
|
+
*
|
|
220
|
+
* @example
|
|
221
|
+
* <caption>Using an authPathPrefix</caption>
|
|
222
|
+
* ```ts
|
|
223
|
+
* // /app/shopify.server.ts
|
|
224
|
+
* import { ApiVersion, shopifyApp } from "@shopify/shopify-app-remix/server";
|
|
225
|
+
*
|
|
226
|
+
* const shopify = shopifyApp({
|
|
227
|
+
* // ...etc
|
|
228
|
+
* authPathPrefix: "/auth",
|
|
229
|
+
* });
|
|
230
|
+
* export default shopify;
|
|
231
|
+
* export const authenticate = shopify.authenticate;
|
|
232
|
+
*
|
|
233
|
+
* // /app/routes/auth/$.jsx
|
|
234
|
+
* import { LoaderFunctionArgs } from "@remix-run/node";
|
|
235
|
+
* import { authenticate } from "../../shopify.server";
|
|
236
|
+
*
|
|
237
|
+
* export async function loader({ request }: LoaderFunctionArgs) {
|
|
238
|
+
* await authenticate.admin(request);
|
|
239
|
+
*
|
|
240
|
+
* return null
|
|
241
|
+
* }
|
|
242
|
+
* ```
|
|
243
|
+
*/
|
|
244
|
+
authPathPrefix?: string;
|
|
245
|
+
|
|
246
|
+
/**
|
|
247
|
+
* Features that will be introduced in future releases of this package.
|
|
248
|
+
*
|
|
249
|
+
* You can opt in to these features by setting the corresponding flags. By doing so, you can prepare for future
|
|
250
|
+
* releases in advance and provide feedback on the new features.
|
|
251
|
+
*/
|
|
252
|
+
future?: Future;
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
export interface AppConfig<
|
|
256
|
+
Storage extends SessionStorage = SessionStorage,
|
|
257
|
+
> extends Omit<ApiConfig, 'future'> {
|
|
258
|
+
canUseLoginForm: boolean;
|
|
259
|
+
appUrl: string;
|
|
260
|
+
auth: AuthConfig;
|
|
261
|
+
sessionStorage?: Storage;
|
|
262
|
+
useOnlineTokens: boolean;
|
|
263
|
+
hooks: HooksConfig;
|
|
264
|
+
future: FutureFlags;
|
|
265
|
+
idempotentPromiseHandler: IdempotentPromiseHandler;
|
|
266
|
+
distribution: AppDistribution;
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
export interface AuthConfig {
|
|
270
|
+
path: string;
|
|
271
|
+
callbackPath: string;
|
|
272
|
+
exitIframePath: string;
|
|
273
|
+
patchSessionTokenPath: string;
|
|
274
|
+
loginPath: string;
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
export type WebhookConfig = Record<string, WebhookHandler | WebhookHandler[]>;
|
|
278
|
+
|
|
279
|
+
interface HooksConfig {
|
|
280
|
+
/**
|
|
281
|
+
* A function to call after a merchant installs your app
|
|
282
|
+
*
|
|
283
|
+
* @param context - An object with context about the request that triggered the hook.
|
|
284
|
+
* @param context.session - The session of the merchant that installed your app. This is the output of sessionStorage.loadSession in case people want to load their own.
|
|
285
|
+
* @param context.admin - An object with access to the Shopify Admin API's.
|
|
286
|
+
*
|
|
287
|
+
* @example
|
|
288
|
+
* <caption>Seeding data when a merchant installs your app.</caption>
|
|
289
|
+
* ```ts
|
|
290
|
+
* import { DeliveryMethod, shopifyApp } from "@shopify/shopify-app-remix/server";
|
|
291
|
+
* import { seedStoreData } from "~/db/seeds"
|
|
292
|
+
*
|
|
293
|
+
* const shopify = shopifyApp({
|
|
294
|
+
* hooks: {
|
|
295
|
+
* afterAuth: async ({ session }) => {
|
|
296
|
+
* seedStoreData({session})
|
|
297
|
+
* }
|
|
298
|
+
* },
|
|
299
|
+
* // ...etc
|
|
300
|
+
* });
|
|
301
|
+
* ```
|
|
302
|
+
*/
|
|
303
|
+
afterAuth?: (options: AfterAuthOptions) => void | Promise<void>;
|
|
304
|
+
}
|
|
305
|
+
|
|
306
|
+
export interface AfterAuthOptions {
|
|
307
|
+
session: Session;
|
|
308
|
+
admin: AdminApiContext;
|
|
309
|
+
}
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
import type {ConfigParams, Shopify} from '@shopify/shopify-api';
|
|
2
|
+
|
|
3
|
+
import {AppConfig} from '../config-types';
|
|
4
|
+
|
|
5
|
+
// When adding new flags, you should also add them to the `TEST_FUTURE_FLAGS` object in `test-config.ts` to ensure that
|
|
6
|
+
// it doesn't cause regressions.
|
|
7
|
+
export interface FutureFlags {
|
|
8
|
+
/**
|
|
9
|
+
* When enabled, embedded apps will fetch access tokens via [token exchange](https://shopify.dev/docs/apps/auth/get-access-tokens/token-exchange).
|
|
10
|
+
* This assumes the app has scopes declared for [Shopify managing installation](https://shopify.dev/docs/apps/auth/installation#shopify-managed-installation).
|
|
11
|
+
*
|
|
12
|
+
* Learn more about this [new embedded app auth strategy](https://shopify.dev/docs/api/shopify-app-remix#embedded-auth-strategy).
|
|
13
|
+
*
|
|
14
|
+
* @default false
|
|
15
|
+
*/
|
|
16
|
+
unstable_newEmbeddedAuthStrategy?: boolean;
|
|
17
|
+
/**
|
|
18
|
+
* When enabled, the app will start using expiring offline access tokens and automatically refresh them when they are close to expiring.
|
|
19
|
+
*
|
|
20
|
+
* @default false
|
|
21
|
+
*/
|
|
22
|
+
expiringOfflineAccessTokens?: boolean;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
// When adding new flags, use this format:
|
|
26
|
+
// apiFutureFlag: Future extends FutureFlags ? Future['remixFutureFlag'] : false;
|
|
27
|
+
export interface ApiFutureFlags<Future extends FutureFlagOptions> {
|
|
28
|
+
expiringOfflineAccessTokens: Future extends FutureFlags
|
|
29
|
+
? Future['expiringOfflineAccessTokens']
|
|
30
|
+
: false;
|
|
31
|
+
unstable_managedPricingSupport: true;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
export type ApiConfigWithFutureFlags<Future extends FutureFlagOptions> =
|
|
35
|
+
ConfigParams<ApiFutureFlags<Future>>;
|
|
36
|
+
|
|
37
|
+
export type FutureFlagOptions = FutureFlags | undefined;
|
|
38
|
+
|
|
39
|
+
export type FeatureEnabled<
|
|
40
|
+
Future extends FutureFlagOptions,
|
|
41
|
+
Flag extends keyof FutureFlags,
|
|
42
|
+
> = Future extends FutureFlags
|
|
43
|
+
? Future[Flag] extends true
|
|
44
|
+
? true
|
|
45
|
+
: false
|
|
46
|
+
: false;
|
|
47
|
+
|
|
48
|
+
export function logDisabledFutureFlags(
|
|
49
|
+
config: AppConfig,
|
|
50
|
+
logger: Shopify['logger'],
|
|
51
|
+
) {
|
|
52
|
+
const logFlag = (flag: string, message: string) =>
|
|
53
|
+
logger.info(`Future flag ${flag} is disabled.\n\n ${message}\n`);
|
|
54
|
+
|
|
55
|
+
if (!config.future.unstable_newEmbeddedAuthStrategy) {
|
|
56
|
+
logFlag(
|
|
57
|
+
'unstable_newEmbeddedAuthStrategy',
|
|
58
|
+
'Enable this to use OAuth token exchange instead of auth code to generate API access tokens.' +
|
|
59
|
+
'\n Your app must be using Shopify managed install: https://shopify.dev/docs/apps/auth/installation',
|
|
60
|
+
);
|
|
61
|
+
}
|
|
62
|
+
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import {AppDistribution, BasicParams} from '../types';
|
|
2
|
+
|
|
3
|
+
export async function createOrLoadOfflineSession(
|
|
4
|
+
{api, config, logger}: BasicParams,
|
|
5
|
+
shop: string,
|
|
6
|
+
) {
|
|
7
|
+
if (config.distribution === AppDistribution.ShopifyAdmin) {
|
|
8
|
+
logger.debug('Creating custom app session from configured access token', {
|
|
9
|
+
shop,
|
|
10
|
+
});
|
|
11
|
+
return api.session.customAppSession(shop);
|
|
12
|
+
} else {
|
|
13
|
+
logger.debug('Loading offline session from session storage', {shop});
|
|
14
|
+
const offlineSessionId = api.session.getOfflineId(shop);
|
|
15
|
+
const session = await config.sessionStorage!.loadSession(offlineSessionId);
|
|
16
|
+
|
|
17
|
+
return session;
|
|
18
|
+
}
|
|
19
|
+
}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import {Session} from '@shopify/shopify-api';
|
|
2
|
+
|
|
3
|
+
import {AppDistribution, BasicParams} from '../types';
|
|
4
|
+
|
|
5
|
+
import refreshToken from './refresh-token';
|
|
6
|
+
|
|
7
|
+
// 5 minutes
|
|
8
|
+
export const WITHIN_MILLISECONDS_OF_EXPIRY = 5 * 60 * 1000;
|
|
9
|
+
|
|
10
|
+
export async function ensureOfflineTokenIsNotExpired(
|
|
11
|
+
session: Session,
|
|
12
|
+
params: BasicParams,
|
|
13
|
+
shop: string,
|
|
14
|
+
) {
|
|
15
|
+
const {config} = params;
|
|
16
|
+
if (
|
|
17
|
+
config.future?.expiringOfflineAccessTokens &&
|
|
18
|
+
session.isExpired(WITHIN_MILLISECONDS_OF_EXPIRY) &&
|
|
19
|
+
config.distribution !== AppDistribution.ShopifyAdmin &&
|
|
20
|
+
session.refreshToken
|
|
21
|
+
) {
|
|
22
|
+
const offlineSession = await refreshToken(
|
|
23
|
+
params,
|
|
24
|
+
shop,
|
|
25
|
+
session.refreshToken,
|
|
26
|
+
);
|
|
27
|
+
|
|
28
|
+
await config.sessionStorage!.storeSession(offlineSession);
|
|
29
|
+
return offlineSession;
|
|
30
|
+
}
|
|
31
|
+
return session;
|
|
32
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import {BasicParams} from '../types';
|
|
2
|
+
|
|
3
|
+
import {createOrLoadOfflineSession} from './create-or-load-offline-session';
|
|
4
|
+
import {ensureOfflineTokenIsNotExpired} from './ensure-offline-token-is-not-expired';
|
|
5
|
+
|
|
6
|
+
export async function ensureValidOfflineSession(
|
|
7
|
+
params: BasicParams,
|
|
8
|
+
shop: string,
|
|
9
|
+
) {
|
|
10
|
+
const session = await createOrLoadOfflineSession(params, shop);
|
|
11
|
+
|
|
12
|
+
if (!session) return undefined;
|
|
13
|
+
|
|
14
|
+
return ensureOfflineTokenIsNotExpired(session, params, shop);
|
|
15
|
+
}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import {
|
|
2
|
+
HttpResponseError,
|
|
3
|
+
InvalidJwtError,
|
|
4
|
+
Session,
|
|
5
|
+
} from '@shopify/shopify-api';
|
|
6
|
+
|
|
7
|
+
import {BasicParams} from '../types';
|
|
8
|
+
|
|
9
|
+
export default async function refreshToken(
|
|
10
|
+
params: BasicParams,
|
|
11
|
+
shop: string,
|
|
12
|
+
refreshToken: string,
|
|
13
|
+
): Promise<Session> {
|
|
14
|
+
const {api} = params;
|
|
15
|
+
try {
|
|
16
|
+
const {session} = await api.auth.refreshToken({
|
|
17
|
+
shop,
|
|
18
|
+
refreshToken,
|
|
19
|
+
});
|
|
20
|
+
return session;
|
|
21
|
+
} catch (error) {
|
|
22
|
+
if (
|
|
23
|
+
error instanceof InvalidJwtError ||
|
|
24
|
+
(error instanceof HttpResponseError &&
|
|
25
|
+
error.response.code === 400 &&
|
|
26
|
+
error.response.body?.error === 'invalid_subject_token')
|
|
27
|
+
) {
|
|
28
|
+
throw error;
|
|
29
|
+
}
|
|
30
|
+
throw new Response(undefined, {
|
|
31
|
+
status: 500,
|
|
32
|
+
statusText: 'Internal Server Error',
|
|
33
|
+
});
|
|
34
|
+
}
|
|
35
|
+
}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import '@shopify/shopify-api/adapters/web-api';
|
|
2
|
+
import {setAbstractRuntimeString} from '@shopify/shopify-api/runtime';
|
|
3
|
+
|
|
4
|
+
setAbstractRuntimeString(() => {
|
|
5
|
+
return `Remix`;
|
|
6
|
+
});
|
|
7
|
+
|
|
8
|
+
export {
|
|
9
|
+
LogSeverity,
|
|
10
|
+
DeliveryMethod,
|
|
11
|
+
BillingInterval,
|
|
12
|
+
BillingReplacementBehavior,
|
|
13
|
+
ApiVersion,
|
|
14
|
+
Session,
|
|
15
|
+
} from '@shopify/shopify-api';
|
|
16
|
+
|
|
17
|
+
export type {JwtPayload} from '@shopify/shopify-api';
|
|
18
|
+
|
|
19
|
+
export type * from './types-contexts';
|
|
20
|
+
export type {ShopifyApp, LoginError} from './types';
|
|
21
|
+
export {LoginErrorType, AppDistribution} from './types';
|
|
22
|
+
export {boundary} from './boundary';
|
|
23
|
+
export {shopifyApp} from './shopify-app';
|
|
24
|
+
export * from './errors';
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import {compare} from 'compare-versions';
|
|
2
|
+
import {FeatureDeprecatedError, Shopify} from '@shopify/shopify-api';
|
|
3
|
+
|
|
4
|
+
import {SHOPIFY_REMIX_LIBRARY_VERSION} from './version';
|
|
5
|
+
|
|
6
|
+
// eslint-disable-next-line no-warning-comments
|
|
7
|
+
// TODO This has been copied from shopify-app-express, it should be extracted into a shared package
|
|
8
|
+
// https://github.com/orgs/Shopify/projects/6899/views/1?pane=issue&itemId=28358070
|
|
9
|
+
export function overrideLogger(logger: Shopify['logger']): Shopify['logger'] {
|
|
10
|
+
const baseContext = {package: 'shopify-app'};
|
|
11
|
+
|
|
12
|
+
const warningFunction: Shopify['logger']['warning'] = (
|
|
13
|
+
message,
|
|
14
|
+
context = {},
|
|
15
|
+
) => logger.warning(message, {...baseContext, ...context});
|
|
16
|
+
|
|
17
|
+
function deprecated(warningFunction: Shopify['logger']['warning']) {
|
|
18
|
+
return function (version: string, message: string): Promise<void> {
|
|
19
|
+
if (compare(SHOPIFY_REMIX_LIBRARY_VERSION, version, '>=')) {
|
|
20
|
+
throw new FeatureDeprecatedError(
|
|
21
|
+
`Feature was deprecated in version ${version}`,
|
|
22
|
+
);
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
return warningFunction(`[Deprecated | ${version}] ${message}`);
|
|
26
|
+
};
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
return {
|
|
30
|
+
...logger,
|
|
31
|
+
log: (severity, message, context = {}) =>
|
|
32
|
+
logger.log(severity, message, {...baseContext, ...context}),
|
|
33
|
+
debug: (message, context = {}) =>
|
|
34
|
+
logger.debug(message, {...baseContext, ...context}),
|
|
35
|
+
info: (message, context = {}) =>
|
|
36
|
+
logger.info(message, {...baseContext, ...context}),
|
|
37
|
+
warning: warningFunction,
|
|
38
|
+
error: (message, context = {}) =>
|
|
39
|
+
logger.error(message, {...baseContext, ...context}),
|
|
40
|
+
deprecated: deprecated(warningFunction),
|
|
41
|
+
};
|
|
42
|
+
}
|