diff --git a/.changeset/popular-goats-join.md b/.changeset/popular-goats-join.md new file mode 100644 index 0000000000..a845151cc8 --- /dev/null +++ b/.changeset/popular-goats-join.md @@ -0,0 +1,2 @@ +--- +--- diff --git a/packages/shopify-app-remix/docs/generated/generated_docs_data.json b/packages/shopify-app-remix/docs/generated/generated_docs_data.json index 37bd5f8366..f7205612cf 100644 --- a/packages/shopify-app-remix/docs/generated/generated_docs_data.json +++ b/packages/shopify-app-remix/docs/generated/generated_docs_data.json @@ -198,13 +198,13 @@ "title": "Using offline sessions", "description": "Get your app's shop-specific data using an offline session.", "tabs": [ - { - "code": "import { shopifyApp } from \"@shopify/shopify-app-remix/server\";\n\nconst shopify = shopifyApp({\n // ...etc\n});\nexport default shopify;\nexport const authenticate = shopify.authenticate;", - "title": "shopify.server.ts" - }, { "code": "import { LoaderFunctionArgs, json } from \"@remix-run/node\";\nimport { authenticate } from \"../shopify.server\";\nimport { getMyAppData } from \"~/db/model.server\";\n\nexport const loader = async ({ request }: LoaderFunctionArgs) => {\n const { session } = await authenticate.admin(request);\n return json(await getMyAppData({shop: session.shop));\n};", "title": "/app/routes/**\\/*.ts" + }, + { + "code": "import { shopifyApp } from \"@shopify/shopify-app-remix/server\";\n\nconst shopify = shopifyApp({\n // ...etc\n});\nexport default shopify;\nexport const authenticate = shopify.authenticate;", + "title": "shopify.server.ts" } ] }, @@ -212,13 +212,13 @@ "title": "Using online sessions", "description": "Get your app's user-specific data using an online session.", "tabs": [ - { - "code": "import { shopifyApp } from \"@shopify/shopify-app-remix/server\";\n\nconst shopify = shopifyApp({\n // ...etc\n useOnlineTokens: true,\n});\nexport default shopify;\nexport const authenticate = shopify.authenticate;", - "title": "shopify.server.ts" - }, { "code": "import { LoaderFunctionArgs, json } from \"@remix-run/node\";\nimport { authenticate } from \"../shopify.server\";\nimport { getMyAppData } from \"~/db/model.server\";\n\nexport const loader = async ({ request }: LoaderFunctionArgs) => {\n const { session } = await authenticate.admin(request);\n return json(await getMyAppData({user: session.onlineAccessInfo!.id}));\n};", "title": "/app/routes/**\\/*.ts" + }, + { + "code": "import { shopifyApp } from \"@shopify/shopify-app-remix/server\";\n\nconst shopify = shopifyApp({\n // ...etc\n useOnlineTokens: true,\n});\nexport default shopify;\nexport const authenticate = shopify.authenticate;", + "title": "shopify.server.ts" } ] } @@ -516,7 +516,7 @@ "description": "Getting the number of orders in a store using REST resources. Visit the [Admin REST API references](/docs/api/admin-rest) for examples on using each resource.", "tabs": [ { - "code": "import { LoaderFunctionArgs, json } from \"@remix-run/node\";\nimport { authenticate } from \"../shopify.server\";\n\nexport const loader = async ({ request }: LoaderFunctionArgs) => {\n const { admin, session } = await authenticate.admin(request);\n return json(admin.rest.resources.Order.count({ session }));\n};", + "code": "import { LoaderFunctionArgs, json } from \"@remix-run/node\";\nimport { authenticate } from \"../shopify.server\";\n\nexport const loader = async ({ request }: LoaderFunctionArgs) => {\n const {\n admin,\n session,\n } = await authenticate.admin(request);\n\n return json(\n admin.rest.resources.Order.count({ session }),\n );\n};", "title": "/app/routes/**\\/*.ts" }, { @@ -530,7 +530,7 @@ "description": "Use `admin.rest.get` to make custom requests to make a request to to the `customer/count` endpoint", "tabs": [ { - "code": "import { LoaderFunctionArgs, json } from \"@remix-run/node\";\nimport { authenticate } from \"../shopify.server\";\n\nexport const loader = async ({ request }: LoaderFunctionArgs) => {\n const { admin, session } = await authenticate.admin(request);\n const response = await admin.rest.get({ path: \"/customers/count.json\" });\n const customers = await response.json();\n return json({ customers });\n};", + "code": "import { LoaderFunctionArgs, json } from \"@remix-run/node\";\nimport { authenticate } from \"../shopify.server\";\n\nexport const loader = async ({ request }: LoaderFunctionArgs) => {\n const {\n admin,\n session,\n } = await authenticate.admin(request);\n\n const response = await admin.rest.get({\n path: \"/customers/count.json\",\n });\n const customers = await response.json();\n\n return json({ customers });\n};", "title": "/app/routes/**\\/*.ts" }, { @@ -544,7 +544,7 @@ "description": "Use `admin.rest.post` to make custom requests to make a request to to the `customers.json` endpoint to send a welcome email", "tabs": [ { - "code": "import { LoaderFunctionArgs, json } from \"@remix-run/node\";\nimport { authenticate } from \"../shopify.server\";\n\nexport const loader = async ({ request }: LoaderFunctionArgs) => {\n const { admin, session } = await authenticate.admin(request);\n const response = admin.rest.post({\n path: \"customers/7392136888625/send_invite.json\",\n body: {\n customer_invite: {\n to: \"new_test_email@shopify.com\",\n from: \"j.limited@example.com\",\n bcc: [\"j.limited@example.com\"],\n subject: \"Welcome to my new shop\",\n custom_message: \"My awesome new store\",\n },\n },\n});\n const customerInvite = await response.json();\n return json({ customerInvite });\n};", + "code": "import { LoaderFunctionArgs, json } from \"@remix-run/node\";\nimport { authenticate } from \"../shopify.server\";\n\nexport const loader = async ({ request }: LoaderFunctionArgs) => {\n const {\n admin,\n session,\n } = await authenticate.admin(request);\n\n const response = admin.rest.post({\n path: \"customers/7392136888625/send_invite.json\",\n body: {\n customer_invite: {\n to: \"new_test_email@shopify.com\",\n from: \"j.limited@example.com\",\n bcc: [\"j.limited@example.com\"],\n subject: \"Welcome to my new shop\",\n custom_message: \"My awesome new store\",\n },\n },\n });\n\n const customerInvite = await response.json();\n return json({ customerInvite });\n};", "title": "/app/routes/**\\/*.ts" }, { @@ -567,11 +567,25 @@ "description": "Use `admin.graphql` to make query / mutation requests.", "tabs": [ { - "code": "import { ActionFunctionArgs } from \"@remix-run/node\";\nimport { authenticate } from \"../shopify.server\";\n\nexport async function action({ request }: ActionFunctionArgs) {\n const { admin } = await authenticate.admin(request);\n\n const response = await admin.graphql(\n `#graphql\n mutation populateProduct($input: ProductInput!) {\n productCreate(input: $input) {\n product {\n id\n }\n }\n }`,\n { variables: { input: { title: \"Product Name\" } } }\n );\n\n const productData = await response.json();\n return json({ data: productData.data });\n}", - "title": "Example" + "code": "import { ActionFunctionArgs } from \"@remix-run/node\";\nimport { authenticate } from \"../shopify.server\";\n\nexport const action = async ({ request }: ActionFunctionArgs) => {\n const { admin } = await authenticate.admin(request);\n\n const response = await admin.graphql(\n `#graphql\n mutation populateProduct($input: ProductInput!) {\n productCreate(input: $input) {\n product {\n id\n }\n }\n }`,\n {\n variables: {\n input: { title: \"Product Name\" },\n },\n },\n );\n\n const productData = await response.json();\n return json({\n productId: productData.data?.productCreate?.product?.id,\n });\n}", + "title": "/app/routes/**\\/*.ts" + }, + { + "code": "import { shopifyApp } from \"@shopify/shopify-app-remix/server\";\n\nconst shopify = shopifyApp({\n // ...\n});\nexport default shopify;\nexport const authenticate = shopify.authenticate;", + "title": "/app/shopify.server.ts" + } + ] + }, + { + "title": "Handling GraphQL errors", + "description": "Catch `GraphqlQueryError` errors to see error messages from the API.", + "tabs": [ + { + "code": "import { ActionFunctionArgs } from \"@remix-run/node\";\nimport { authenticate } from \"../shopify.server\";\n\nexport const action = async ({ request }: ActionFunctionArgs) => {\n const { admin } = await authenticate.admin(request);\n\n try {\n const response = await admin.graphql(\n `#graphql\n query incorrectQuery {\n products(first: 10) {\n nodes {\n not_a_field\n }\n }\n }`,\n );\n\n return json({ data: await response.json() });\n } catch (error) {\n if (error instanceof GraphqlQueryError) {\n // error.body.errors:\n // { graphQLErrors: [\n // { message: \"Field 'not_a_field' doesn't exist on type 'Product'\" }\n // ] }\n return json({ errors: error.body?.errors }, { status: 500 });\n }\n return json({ message: \"An error occurred\" }, { status: 500 });\n }\n}", + "title": "/app/routes/**\\/*.ts" }, { - "code": "import { shopifyApp } from \"@shopify/shopify-app-remix/server\";\n\nconst shopify = shopifyApp({\n restResources,\n // ...etc\n});\nexport default shopify;\nexport const unauthenticated = shopify.unauthenticated;", + "code": "import { shopifyApp } from \"@shopify/shopify-app-remix/server\";\n\nconst shopify = shopifyApp({\n // ...\n});\nexport default shopify;\nexport const authenticate = shopify.authenticate;", "title": "/app/shopify.server.ts" } ] @@ -579,7 +593,7 @@ ] } ], - "value": "export interface AdminApiContext<\n Resources extends ShopifyRestResources = ShopifyRestResources,\n> {\n /**\n * Methods for interacting with the Shopify Admin REST API\n *\n * There are methods for interacting with individual REST resources. You can also make `GET`, `POST`, `PUT` and `DELETE` requests should the REST resources not meet your needs.\n *\n * {@link https://shopify.dev/docs/api/admin-rest}\n *\n * @example\n * Using REST resources.\n * Getting the number of orders in a store using REST resources. Visit the [Admin REST API references](/docs/api/admin-rest) for examples on using each resource. \n *\n * ```ts\n * // /app/routes/**\\/*.ts\n * import { LoaderFunctionArgs, json } from \"@remix-run/node\";\n * import { authenticate } from \"../shopify.server\";\n *\n * export const loader = async ({ request }: LoaderFunctionArgs) => {\n * const { admin, session } = await authenticate.admin(request);\n * return json(admin.rest.resources.Order.count({ session }));\n * };\n * ```\n *\n *\n * ```ts\n * // /app/shopify.server.ts\n * import { shopifyApp } from \"@shopify/shopify-app-remix/server\";\n * import { restResources } from \"@shopify/shopify-api/rest/admin/2023-07\";\n *\n * const shopify = shopifyApp({\n * restResources,\n * // ...etc\n * });\n * export default shopify;\n * export const authenticate = shopify.authenticate;\n * ```\n *\n *\n * @example\n * Performing a GET request to the REST API.\n * Use `admin.rest.get` to make custom requests to make a request to to the `customer/count` endpoint\n *\n * ```ts\n * // /app/routes/**\\/*.ts\n * import { LoaderFunctionArgs, json } from \"@remix-run/node\";\n * import { authenticate } from \"../shopify.server\";\n *\n * export const loader = async ({ request }: LoaderFunctionArgs) => {\n * const { admin, session } = await authenticate.admin(request);\n * const response = await admin.rest.get({ path: \"/customers/count.json\" });\n * const customers = await response.json();\n * return json({ customers });\n * };\n * ```\n *\n * ```ts\n * // /app/shopify.server.ts\n * import { shopifyApp } from \"@shopify/shopify-app-remix/server\";\n * import { restResources } from \"@shopify/shopify-api/rest/admin/2023-04\";\n *\n * const shopify = shopifyApp({\n * restResources,\n * // ...etc\n * });\n * export default shopify;\n * export const authenticate = shopify.authenticate;\n * ```\n * @example\n * Performing a POST request to the REST API.\n * Use `admin.rest.post` to make custom requests to make a request to to the `customers.json` endpoint to send a welcome email\n * ```ts\n * // /app/routes/**\\/*.ts\n * import { LoaderFunctionArgs, json } from \"@remix-run/node\";\n * import { authenticate } from \"../shopify.server\";\n *\n * export const loader = async ({ request }: LoaderFunctionArgs) => {\n * const { admin, session } = await authenticate.admin(request);\n * const response = admin.rest.post({\n * path: \"customers/7392136888625/send_invite.json\",\n * body: {\n * customer_invite: {\n * to: \"new_test_email@shopify.com\",\n * from: \"j.limited@example.com\",\n * bcc: [\"j.limited@example.com\"],\n * subject: \"Welcome to my new shop\",\n * custom_message: \"My awesome new store\",\n * },\n * },\n * });\n * const customerInvite = await response.json();\n * return json({ customerInvite });\n * };\n * ```\n *\n * ```ts\n * // /app/shopify.server.ts\n * import { shopifyApp } from \"@shopify/shopify-app-remix/server\";\n * import { restResources } from \"@shopify/shopify-api/rest/admin/2023-04\";\n *\n * const shopify = shopifyApp({\n * restResources,\n * // ...etc\n * });\n * export default shopify;\n * export const authenticate = shopify.authenticate;\n * ```\n */\n rest: RestClientWithResources;\n\n /**\n * Methods for interacting with the Shopify Admin GraphQL API\n *\n * {@link https://shopify.dev/docs/api/admin-graphql}\n * {@link https://github.com/Shopify/shopify-api-js/blob/main/packages/shopify-api/docs/reference/clients/Graphql.md}\n *\n * @example\n * Querying the GraphQL API.\n * Use `admin.graphql` to make query / mutation requests.\n * ```ts\n * import { ActionFunctionArgs } from \"@remix-run/node\";\n * import { authenticate } from \"../shopify.server\";\n *\n * export async function action({ request }: ActionFunctionArgs) {\n * const { admin } = await authenticate.admin(request);\n *\n * const response = await admin.graphql(\n * `#graphql\n * mutation populateProduct($input: ProductInput!) {\n * productCreate(input: $input) {\n * product {\n * id\n * }\n * }\n * }`,\n * { variables: { input: { title: \"Product Name\" } } }\n * );\n *\n * const productData = await response.json();\n * return json({ data: productData.data });\n * }\n * ```\n *\n * ```ts\n * // /app/shopify.server.ts\n * import { shopifyApp } from \"@shopify/shopify-app-remix/server\";\n *\n * const shopify = shopifyApp({\n * restResources,\n * // ...etc\n * });\n * export default shopify;\n * export const unauthenticated = shopify.unauthenticated;\n * ```\n */\n graphql: GraphQLClient;\n}" + "value": "export interface AdminApiContext<\n Resources extends ShopifyRestResources = ShopifyRestResources,\n> {\n /**\n * Methods for interacting with the Shopify Admin REST API\n *\n * There are methods for interacting with individual REST resources. You can also make `GET`, `POST`, `PUT` and `DELETE` requests should the REST resources not meet your needs.\n *\n * {@link https://shopify.dev/docs/api/admin-rest}\n *\n * @example\n * Using REST resources.\n * Getting the number of orders in a store using REST resources. Visit the [Admin REST API references](/docs/api/admin-rest) for examples on using each resource. \n *\n * ```ts\n * // /app/routes/**\\/*.ts\n * import { LoaderFunctionArgs, json } from \"@remix-run/node\";\n * import { authenticate } from \"../shopify.server\";\n *\n * export const loader = async ({ request }: LoaderFunctionArgs) => {\n * const {\n * admin,\n * session,\n * } = await authenticate.admin(request);\n *\n * return json(\n * admin.rest.resources.Order.count({ session }),\n * );\n * };\n * ```\n *\n * ```ts\n * // /app/shopify.server.ts\n * import { shopifyApp } from \"@shopify/shopify-app-remix/server\";\n * import { restResources } from \"@shopify/shopify-api/rest/admin/2023-07\";\n *\n * const shopify = shopifyApp({\n * restResources,\n * // ...etc\n * });\n * export default shopify;\n * export const authenticate = shopify.authenticate;\n * ```\n *\n * @example\n * Performing a GET request to the REST API.\n * Use `admin.rest.get` to make custom requests to make a request to to the `customer/count` endpoint\n *\n * ```ts\n * // /app/routes/**\\/*.ts\n * import { LoaderFunctionArgs, json } from \"@remix-run/node\";\n * import { authenticate } from \"../shopify.server\";\n *\n * export const loader = async ({ request }: LoaderFunctionArgs) => {\n * const {\n * admin,\n * session,\n * } = await authenticate.admin(request);\n *\n * const response = await admin.rest.get({\n * path: \"/customers/count.json\",\n * });\n * const customers = await response.json();\n *\n * return json({ customers });\n * };\n * ```\n *\n * ```ts\n * // /app/shopify.server.ts\n * import { shopifyApp } from \"@shopify/shopify-app-remix/server\";\n * import { restResources } from \"@shopify/shopify-api/rest/admin/2023-04\";\n *\n * const shopify = shopifyApp({\n * restResources,\n * // ...etc\n * });\n * export default shopify;\n * export const authenticate = shopify.authenticate;\n * ```\n *\n * @example\n * Performing a POST request to the REST API.\n * Use `admin.rest.post` to make custom requests to make a request to to the `customers.json` endpoint to send a welcome email\n * ```ts\n * // /app/routes/**\\/*.ts\n * import { LoaderFunctionArgs, json } from \"@remix-run/node\";\n * import { authenticate } from \"../shopify.server\";\n *\n * export const loader = async ({ request }: LoaderFunctionArgs) => {\n * const {\n * admin,\n * session,\n * } = await authenticate.admin(request);\n *\n * const response = admin.rest.post({\n * path: \"customers/7392136888625/send_invite.json\",\n * body: {\n * customer_invite: {\n * to: \"new_test_email@shopify.com\",\n * from: \"j.limited@example.com\",\n * bcc: [\"j.limited@example.com\"],\n * subject: \"Welcome to my new shop\",\n * custom_message: \"My awesome new store\",\n * },\n * },\n * });\n *\n * const customerInvite = await response.json();\n * return json({ customerInvite });\n * };\n * ```\n *\n * ```ts\n * // /app/shopify.server.ts\n * import { shopifyApp } from \"@shopify/shopify-app-remix/server\";\n * import { restResources } from \"@shopify/shopify-api/rest/admin/2023-04\";\n *\n * const shopify = shopifyApp({\n * restResources,\n * // ...etc\n * });\n * export default shopify;\n * export const authenticate = shopify.authenticate;\n * ```\n */\n rest: RestClientWithResources;\n\n /**\n * Methods for interacting with the Shopify Admin GraphQL API\n *\n * {@link https://shopify.dev/docs/api/admin-graphql}\n * {@link https://github.com/Shopify/shopify-api-js/blob/main/packages/shopify-api/docs/reference/clients/Graphql.md}\n *\n * @example\n * Querying the GraphQL API.\n * Use `admin.graphql` to make query / mutation requests.\n * ```ts\n * // /app/routes/**\\/*.ts\n * import { ActionFunctionArgs } from \"@remix-run/node\";\n * import { authenticate } from \"../shopify.server\";\n *\n * export const action = async ({ request }: ActionFunctionArgs) => {\n * const { admin } = await authenticate.admin(request);\n *\n * const response = await admin.graphql(\n * `#graphql\n * mutation populateProduct($input: ProductInput!) {\n * productCreate(input: $input) {\n * product {\n * id\n * }\n * }\n * }`,\n * {\n * variables: {\n * input: { title: \"Product Name\" },\n * },\n * },\n * );\n *\n * const productData = await response.json();\n * return json({\n * productId: productData.data?.productCreate?.product?.id,\n * });\n * }\n * ```\n *\n * ```ts\n * // /app/shopify.server.ts\n * import { shopifyApp } from \"@shopify/shopify-app-remix/server\";\n *\n * const shopify = shopifyApp({\n * // ...\n * });\n * export default shopify;\n * export const authenticate = shopify.authenticate;\n * ```\n *\n * @example\n * Handling GraphQL errors.\n * Catch `GraphqlQueryError` errors to see error messages from the API.\n * ```ts\n * // /app/routes/**\\/*.ts\n * import { ActionFunctionArgs } from \"@remix-run/node\";\n * import { authenticate } from \"../shopify.server\";\n *\n * export const action = async ({ request }: ActionFunctionArgs) => {\n * const { admin } = await authenticate.admin(request);\n *\n * try {\n * const response = await admin.graphql(\n * `#graphql\n * query incorrectQuery {\n * products(first: 10) {\n * nodes {\n * not_a_field\n * }\n * }\n * }`,\n * );\n *\n * return json({ data: await response.json() });\n * } catch (error) {\n * if (error instanceof GraphqlQueryError) {\n * // error.body.errors:\n * // { graphQLErrors: [\n * // { message: \"Field 'not_a_field' doesn't exist on type 'Product'\" }\n * // ] }\n * return json({ errors: error.body?.errors }, { status: 500 });\n * }\n * return json({ message: \"An error occurred\" }, { status: 500 });\n * }\n * }\n * ```\n *\n * ```ts\n * // /app/shopify.server.ts\n * import { shopifyApp } from \"@shopify/shopify-app-remix/server\";\n *\n * const shopify = shopifyApp({\n * // ...\n * });\n * export default shopify;\n * export const authenticate = shopify.authenticate;\n * ```\n */\n graphql: GraphQLClient;\n}" }, "RestClientWithResources": { "filePath": "src/server/clients/admin/rest.ts", @@ -732,6 +746,116 @@ "value": "GetRequestParams & {\n data: Record | string;\n}", "description": "" }, + "GraphQLClient": { + "filePath": "src/server/clients/types.ts", + "name": "GraphQLClient", + "description": "", + "params": [ + { + "name": "query", + "description": "", + "value": "Operation extends keyof Operations", + "filePath": "src/server/clients/types.ts" + }, + { + "name": "options", + "description": "", + "value": "GraphQLQueryOptions", + "isOptional": true, + "filePath": "src/server/clients/types.ts" + } + ], + "returns": { + "filePath": "src/server/clients/types.ts", + "description": "", + "name": "interface Promise {\n /**\n * Attaches callbacks for the resolution and/or rejection of the Promise.\n * @param onfulfilled The callback to execute when the Promise is resolved.\n * @param onrejected The callback to execute when the Promise is rejected.\n * @returns A Promise for the completion of which ever callback is executed.\n */\n then(onfulfilled?: ((value: T) => TResult1 | PromiseLike) | undefined | null, onrejected?: ((reason: any) => TResult2 | PromiseLike) | undefined | null): Promise;\n\n /**\n * Attaches a callback for only the rejection of the Promise.\n * @param onrejected The callback to execute when the Promise is rejected.\n * @returns A Promise for the completion of the callback.\n */\n catch(onrejected?: ((reason: any) => TResult | PromiseLike) | undefined | null): Promise;\n}, interface Promise {}, Promise: PromiseConstructor, interface Promise {\n readonly [Symbol.toStringTag]: string;\n}, interface Promise {\n /**\n * Attaches a callback that is invoked when the Promise is settled (fulfilled or rejected). The\n * resolved value cannot be modified from the callback.\n * @param onfinally The callback to execute when the Promise is settled (fulfilled or rejected).\n * @returns A Promise for the completion of the callback.\n */\n finally(onfinally?: (() => void) | undefined | null): Promise;\n}", + "value": "interface Promise {\n /**\n * Attaches callbacks for the resolution and/or rejection of the Promise.\n * @param onfulfilled The callback to execute when the Promise is resolved.\n * @param onrejected The callback to execute when the Promise is rejected.\n * @returns A Promise for the completion of which ever callback is executed.\n */\n then(onfulfilled?: ((value: T) => TResult1 | PromiseLike) | undefined | null, onrejected?: ((reason: any) => TResult2 | PromiseLike) | undefined | null): Promise;\n\n /**\n * Attaches a callback for only the rejection of the Promise.\n * @param onrejected The callback to execute when the Promise is rejected.\n * @returns A Promise for the completion of the callback.\n */\n catch(onrejected?: ((reason: any) => TResult | PromiseLike) | undefined | null): Promise;\n}, interface Promise {}, Promise: PromiseConstructor, interface Promise {\n readonly [Symbol.toStringTag]: string;\n}, interface Promise {\n /**\n * Attaches a callback that is invoked when the Promise is settled (fulfilled or rejected). The\n * resolved value cannot be modified from the callback.\n * @param onfinally The callback to execute when the Promise is settled (fulfilled or rejected).\n * @returns A Promise for the completion of the callback.\n */\n finally(onfinally?: (() => void) | undefined | null): Promise;\n}" + }, + "value": "export type GraphQLClient = <\n Operation extends keyof Operations,\n>(\n query: Operation,\n options?: GraphQLQueryOptions,\n) => Promise>;" + }, + "GraphQLQueryOptions": { + "filePath": "src/server/clients/types.ts", + "name": "GraphQLQueryOptions", + "description": "", + "members": [ + { + "filePath": "src/server/clients/types.ts", + "syntaxKind": "PropertySignature", + "name": "variables", + "value": "ApiClientRequestOptions[\"variables\"]", + "description": "The variables to pass to the operation.", + "isOptional": true + }, + { + "filePath": "src/server/clients/types.ts", + "syntaxKind": "PropertySignature", + "name": "apiVersion", + "value": "ApiVersion", + "description": "The version of the API to use for the request.", + "isOptional": true + }, + { + "filePath": "src/server/clients/types.ts", + "syntaxKind": "PropertySignature", + "name": "headers", + "value": "Record", + "description": "Additional headers to include in the request.", + "isOptional": true + }, + { + "filePath": "src/server/clients/types.ts", + "syntaxKind": "PropertySignature", + "name": "tries", + "value": "number", + "description": "The total number of times to try the request if it fails.", + "isOptional": true + } + ], + "value": "export interface GraphQLQueryOptions<\n Operation extends keyof Operations,\n Operations extends AllOperations,\n> {\n /**\n * The variables to pass to the operation.\n */\n variables?: ApiClientRequestOptions['variables'];\n /**\n * The version of the API to use for the request.\n */\n apiVersion?: ApiVersion;\n /**\n * Additional headers to include in the request.\n */\n headers?: Record;\n /**\n * The total number of times to try the request if it fails.\n */\n tries?: number;\n}" + }, + "ApiVersion": { + "filePath": "../../node_modules/@shopify/shopify-api/lib/types.d.ts", + "syntaxKind": "EnumDeclaration", + "name": "ApiVersion", + "value": "export declare enum ApiVersion {\n October22 = \"2022-10\",\n January23 = \"2023-01\",\n April23 = \"2023-04\",\n July23 = \"2023-07\",\n October23 = \"2023-10\",\n January24 = \"2024-01\",\n Unstable = \"unstable\"\n}", + "members": [ + { + "filePath": "../../node_modules/@shopify/shopify-api/lib/types.d.ts", + "name": "October22", + "value": "2022-10" + }, + { + "filePath": "../../node_modules/@shopify/shopify-api/lib/types.d.ts", + "name": "January23", + "value": "2023-01" + }, + { + "filePath": "../../node_modules/@shopify/shopify-api/lib/types.d.ts", + "name": "April23", + "value": "2023-04" + }, + { + "filePath": "../../node_modules/@shopify/shopify-api/lib/types.d.ts", + "name": "July23", + "value": "2023-07" + }, + { + "filePath": "../../node_modules/@shopify/shopify-api/lib/types.d.ts", + "name": "October23", + "value": "2023-10" + }, + { + "filePath": "../../node_modules/@shopify/shopify-api/lib/types.d.ts", + "name": "January24", + "value": "2024-01" + }, + { + "filePath": "../../node_modules/@shopify/shopify-api/lib/types.d.ts", + "name": "Unstable", + "value": "unstable" + } + ] + }, "AdminOperations": { "filePath": "../../node_modules/@shopify/shopify-api/node_modules/@shopify/admin-api-client/dist/ts/graphql/types.d.ts", "syntaxKind": "TypeAliasDeclaration", @@ -824,7 +948,7 @@ "title": "/app/routes/**\\/*.ts" }, { - "code": "import { shopifyApp, BillingInterval } from \"@shopify/shopify-app-remix/server\";\n\nexport const MONTHLY_PLAN = 'Monthly subscription';\nexport const ANNUAL_PLAN = 'Annual subscription';\n\nconst shopify = shopifyApp({\n // ...etc\n billing: {\n [MONTHLY_PLAN]: {\n lineItems: [\n {\n amount: 5,\n currencyCode: 'USD',\n interval: BillingInterval.Every30Days,\n }\n {\n amount: 1,\n currencyCode: 'USD',\n interval: BillingInterval.Usage.\n terms: '1 dollar per 1000 emails',\n },\n ],\n },\n }\n future: {v3_lineItemBilling: true}\n});\nexport default shopify;\nexport const authenticate = shopify.authenticate;", + "code": "import { shopifyApp, BillingInterval } from \"@shopify/shopify-app-remix/server\";\n\nexport const MONTHLY_PLAN = 'Monthly subscription';\nexport const ANNUAL_PLAN = 'Annual subscription';\n\nconst shopify = shopifyApp({\n // ...etc\n billing: {\n [MONTHLY_PLAN]: {\n lineItems: [\n {\n amount: 5,\n currencyCode: 'USD',\n interval: BillingInterval.Every30Days,\n },\n {\n amount: 1,\n currencyCode: 'USD',\n interval: BillingInterval.Usage.\n terms: '1 dollar per 1000 emails',\n },\n ],\n },\n }\n future: {v3_lineItemBilling: true}\n});\nexport default shopify;\nexport const authenticate = shopify.authenticate;", "title": "shopify.server.ts" } ] @@ -901,7 +1025,7 @@ ] } ], - "value": "export interface BillingContext {\n /**\n * Checks if the shop has an active payment for any plan defined in the `billing` config option.\n *\n * @returns A promise that resolves to an object containing the active purchases for the shop.\n *\n * @example\n * Requesting billing right away.\n * Call `billing.request` in the `onFailure` callback to immediately redirect to the Shopify page to request payment.\n * ```ts\n * // /app/routes/**\\/*.ts\n * import { LoaderFunctionArgs } from \"@remix-run/node\";\n * import { authenticate, MONTHLY_PLAN } from \"../shopify.server\";\n *\n * export const loader = async ({ request }: LoaderFunctionArgs) => {\n * const { billing } = await authenticate.admin(request);\n * await billing.require({\n * plans: [MONTHLY_PLAN],\n * isTest: true,\n * onFailure: async () => billing.request({ plan: MONTHLY_PLAN }),\n * });\n *\n * // App logic\n * };\n * ```\n * ```ts\n * // shopify.server.ts\n * import { shopifyApp, BillingInterval } from \"@shopify/shopify-app-remix/server\";\n *\n * export const MONTHLY_PLAN = 'Monthly subscription';\n * export const ANNUAL_PLAN = 'Annual subscription';\n *\n * const shopify = shopifyApp({\n * // ...etc\n * billing: {\n * [MONTHLY_PLAN]: {\n * amount: 5,\n * currencyCode: 'USD',\n * interval: BillingInterval.Every30Days,\n * },\n * [ANNUAL_PLAN]: {\n * amount: 50,\n * currencyCode: 'USD',\n * interval: BillingInterval.Annual,\n * },\n * }\n * });\n * export default shopify;\n * export const authenticate = shopify.authenticate;\n * ```\n *\n * @example\n * Redirect to a plan selection page.\n * When the app has multiple plans, create a page in your App that allows the merchant to select a plan. If a merchant does not have the required plan you can redirect them to page in your app to select one.\n * ```ts\n * // /app/routes/**\\/*.ts\n * import { LoaderFunctionArgs, redirect } from \"@remix-run/node\";\n * import { authenticate, MONTHLY_PLAN, ANNUAL_PLAN } from \"../shopify.server\";\n *\n * export const loader = async ({ request }: LoaderFunctionArgs) => {\n * const { billing } = await authenticate.admin(request);\n * const billingCheck = await billing.require({\n * plans: [MONTHLY_PLAN, ANNUAL_PLAN],\n * isTest: true,\n * onFailure: () => redirect('/select-plan'),\n * });\n *\n * const subscription = billingCheck.appSubscriptions[0];\n * console.log(`Shop is on ${subscription.name} (id ${subscription.id})`);\n *\n * // App logic\n * };\n * ```\n * ```ts\n * // shopify.server.ts\n * import { shopifyApp, BillingInterval } from \"@shopify/shopify-app-remix/server\";\n *\n * export const MONTHLY_PLAN = 'Monthly subscription';\n * export const ANNUAL_PLAN = 'Annual subscription';\n *\n * const shopify = shopifyApp({\n * // ...etc\n * billing: {\n * [MONTHLY_PLAN]: {\n * amount: 5,\n * currencyCode: 'USD',\n * interval: BillingInterval.Every30Days,\n * },\n * [ANNUAL_PLAN]: {\n * amount: 50,\n * currencyCode: 'USD',\n * interval: BillingInterval.Annual,\n * },\n * }\n * });\n * export default shopify;\n * export const authenticate = shopify.authenticate;\n * ```\n * @example\n * Requesting billing with line items\n * Call `billing.request` with the `v3_lineItemBilling` future flag enabled\n * ```ts\n * // /app/routes/**\\/*.ts\n * import { LoaderFunctionArgs } from \"@remix-run/node\";\n * import { authenticate, MONTHLY_PLAN } from \"../shopify.server\";\n *\n * export const loader = async ({ request }: LoaderFunctionArgs) => {\n * const { billing } = await authenticate.admin(request);\n * await billing.require({\n * plans: [MONTHLY_PLAN],\n * isTest: true,\n * onFailure: async () => billing.request({ plan: MONTHLY_PLAN }),\n * });\n *\n * // App logic\n * };\n * ```\n * ```ts\n * // shopify.server.ts\n * import { shopifyApp, BillingInterval } from \"@shopify/shopify-app-remix/server\";\n *\n * export const MONTHLY_PLAN = 'Monthly subscription';\n * export const ANNUAL_PLAN = 'Annual subscription';\n *\n * const shopify = shopifyApp({\n * // ...etc\n * billing: {\n * [MONTHLY_PLAN]: {\n * lineItems: [\n * {\n * amount: 5,\n * currencyCode: 'USD',\n * interval: BillingInterval.Every30Days,\n * }\n * {\n * amount: 1,\n * currencyCode: 'USD',\n * interval: BillingInterval.Usage.\n * terms: '1 dollar per 1000 emails',\n * },\n * ],\n * },\n * }\n * future: {v3_lineItemBilling: true}\n * });\n * export default shopify;\n * export const authenticate = shopify.authenticate;\n * ```\n */\n require: (\n options: RequireBillingOptions,\n ) => Promise;\n\n /**\n * Checks if the shop has an active payment for any plan defined in the `billing` config option.\n *\n * @returns A promise that resolves to an object containing the active purchases for the shop.\n *\n * @example\n * Check what billing plans a merchant is subscribed to.\n * Use billing.check if you want to determine which plans are in use. Unlike `require`, `check` does not\n * throw an error if no active billing plans are present. \n * ```ts\n * // /app/routes/**\\/*.ts\n * import { LoaderFunctionArgs } from \"@remix-run/node\";\n * import { authenticate, MONTHLY_PLAN } from \"../shopify.server\";\n *\n * export const loader = async ({ request }: LoaderFunctionArgs) => {\n * const { billing } = await authenticate.admin(request);\n * const { hasActivePayment, appSubscriptions } = await billing.check({\n * plans: [MONTHLY_PLAN],\n * isTest: false,\n * });\n * console.log(hasActivePayment)\n * console.log(appSubscriptions)\n * };\n * ```\n * ```ts\n * // shopify.server.ts\n * import { shopifyApp, BillingInterval } from \"@shopify/shopify-app-remix/server\";\n *\n * export const MONTHLY_PLAN = 'Monthly subscription';\n * export const ANNUAL_PLAN = 'Annual subscription';\n *\n * const shopify = shopifyApp({\n * // ...etc\n * billing: {\n * [MONTHLY_PLAN]: {\n * amount: 5,\n * currencyCode: 'USD',\n * interval: BillingInterval.Every30Days,\n * },\n * [ANNUAL_PLAN]: {\n * amount: 50,\n * currencyCode: 'USD',\n * interval: BillingInterval.Annual,\n * },\n * }\n * });\n * export default shopify;\n * export const authenticate = shopify.authenticate;\n * ```\n *\n */\n check: (\n options: CheckBillingOptions,\n ) => Promise;\n\n /**\n * Requests payment for the plan.\n *\n * @returns Redirects to the confirmation URL for the payment.\n *\n * @example\n * Using a custom return URL.\n * Change where the merchant is returned to after approving the purchase using the `returnUrl` option.\n * ```ts\n * // /app/routes/**\\/*.ts\n * import { LoaderFunctionArgs } from \"@remix-run/node\";\n * import { authenticate, MONTHLY_PLAN } from \"../shopify.server\";\n *\n * export const loader = async ({ request }: LoaderFunctionArgs) => {\n * const { billing } = await authenticate.admin(request);\n * await billing.require({\n * plans: [MONTHLY_PLAN],\n * onFailure: async () => billing.request({\n * plan: MONTHLY_PLAN,\n * isTest: true,\n * returnUrl: 'https://admin.shopify.com/store/my-store/apps/my-app/billing-page',\n * }),\n * });\n *\n * // App logic\n * };\n * ```\n * ```ts\n * // shopify.server.ts\n * import { shopifyApp, BillingInterval } from \"@shopify/shopify-app-remix/server\";\n *\n * export const MONTHLY_PLAN = 'Monthly subscription';\n * export const ANNUAL_PLAN = 'Annual subscription';\n *\n * const shopify = shopifyApp({\n * // ...etc\n * billing: {\n * [MONTHLY_PLAN]: {\n * amount: 5,\n * currencyCode: 'USD',\n * interval: BillingInterval.Every30Days,\n * },\n * [ANNUAL_PLAN]: {\n * amount: 50,\n * currencyCode: 'USD',\n * interval: BillingInterval.Annual,\n * },\n * }\n * });\n * export default shopify;\n * export const authenticate = shopify.authenticate;\n * ```\n */\n request: (options: RequestBillingOptions) => Promise;\n\n /**\n * Cancels an ongoing subscription, given its ID.\n *\n * @returns The cancelled subscription.\n *\n * @example\n * Cancelling a subscription.\n * Use the `billing.cancel` function to cancel an active subscription with the id returned from `billing.require`.\n * ```ts\n * // /app/routes/cancel-subscription.ts\n * import { LoaderFunctionArgs } from \"@remix-run/node\";\n * import { authenticate, MONTHLY_PLAN } from \"../shopify.server\";\n *\n * export const loader = async ({ request }: LoaderFunctionArgs) => {\n * const { billing } = await authenticate.admin(request);\n * const billingCheck = await billing.require({\n * plans: [MONTHLY_PLAN],\n * onFailure: async () => billing.request({ plan: MONTHLY_PLAN }),\n * });\n *\n * const subscription = billingCheck.appSubscriptions[0];\n * const cancelledSubscription = await billing.cancel({\n * subscriptionId: subscription.id,\n * isTest: true,\n * prorate: true,\n * });\n *\n * // App logic\n * };\n * ```\n * ```ts\n * // shopify.server.ts\n * import { shopifyApp, BillingInterval } from \"@shopify/shopify-app-remix/server\";\n *\n * export const MONTHLY_PLAN = 'Monthly subscription';\n * export const ANNUAL_PLAN = 'Annual subscription';\n *\n * const shopify = shopifyApp({\n * // ...etc\n * billing: {\n * [MONTHLY_PLAN]: {\n * amount: 5,\n * currencyCode: 'USD',\n * interval: BillingInterval.Every30Days,\n * },\n * [ANNUAL_PLAN]: {\n * amount: 50,\n * currencyCode: 'USD',\n * interval: BillingInterval.Annual,\n * },\n * }\n * });\n * export default shopify;\n * export const authenticate = shopify.authenticate;\n * ```\n */\n cancel: (options: CancelBillingOptions) => Promise;\n}" + "value": "export interface BillingContext {\n /**\n * Checks if the shop has an active payment for any plan defined in the `billing` config option.\n *\n * @returns A promise that resolves to an object containing the active purchases for the shop.\n *\n * @example\n * Requesting billing right away.\n * Call `billing.request` in the `onFailure` callback to immediately redirect to the Shopify page to request payment.\n * ```ts\n * // /app/routes/**\\/*.ts\n * import { LoaderFunctionArgs } from \"@remix-run/node\";\n * import { authenticate, MONTHLY_PLAN } from \"../shopify.server\";\n *\n * export const loader = async ({ request }: LoaderFunctionArgs) => {\n * const { billing } = await authenticate.admin(request);\n * await billing.require({\n * plans: [MONTHLY_PLAN],\n * isTest: true,\n * onFailure: async () => billing.request({ plan: MONTHLY_PLAN }),\n * });\n *\n * // App logic\n * };\n * ```\n * ```ts\n * // shopify.server.ts\n * import { shopifyApp, BillingInterval } from \"@shopify/shopify-app-remix/server\";\n *\n * export const MONTHLY_PLAN = 'Monthly subscription';\n * export const ANNUAL_PLAN = 'Annual subscription';\n *\n * const shopify = shopifyApp({\n * // ...etc\n * billing: {\n * [MONTHLY_PLAN]: {\n * amount: 5,\n * currencyCode: 'USD',\n * interval: BillingInterval.Every30Days,\n * },\n * [ANNUAL_PLAN]: {\n * amount: 50,\n * currencyCode: 'USD',\n * interval: BillingInterval.Annual,\n * },\n * }\n * });\n * export default shopify;\n * export const authenticate = shopify.authenticate;\n * ```\n *\n * @example\n * Redirect to a plan selection page.\n * When the app has multiple plans, create a page in your App that allows the merchant to select a plan. If a merchant does not have the required plan you can redirect them to page in your app to select one.\n * ```ts\n * // /app/routes/**\\/*.ts\n * import { LoaderFunctionArgs, redirect } from \"@remix-run/node\";\n * import { authenticate, MONTHLY_PLAN, ANNUAL_PLAN } from \"../shopify.server\";\n *\n * export const loader = async ({ request }: LoaderFunctionArgs) => {\n * const { billing } = await authenticate.admin(request);\n * const billingCheck = await billing.require({\n * plans: [MONTHLY_PLAN, ANNUAL_PLAN],\n * isTest: true,\n * onFailure: () => redirect('/select-plan'),\n * });\n *\n * const subscription = billingCheck.appSubscriptions[0];\n * console.log(`Shop is on ${subscription.name} (id ${subscription.id})`);\n *\n * // App logic\n * };\n * ```\n * ```ts\n * // shopify.server.ts\n * import { shopifyApp, BillingInterval } from \"@shopify/shopify-app-remix/server\";\n *\n * export const MONTHLY_PLAN = 'Monthly subscription';\n * export const ANNUAL_PLAN = 'Annual subscription';\n *\n * const shopify = shopifyApp({\n * // ...etc\n * billing: {\n * [MONTHLY_PLAN]: {\n * amount: 5,\n * currencyCode: 'USD',\n * interval: BillingInterval.Every30Days,\n * },\n * [ANNUAL_PLAN]: {\n * amount: 50,\n * currencyCode: 'USD',\n * interval: BillingInterval.Annual,\n * },\n * }\n * });\n * export default shopify;\n * export const authenticate = shopify.authenticate;\n * ```\n * @example\n * Requesting billing with line items\n * Call `billing.request` with the `v3_lineItemBilling` future flag enabled\n * ```ts\n * // /app/routes/**\\/*.ts\n * import { LoaderFunctionArgs } from \"@remix-run/node\";\n * import { authenticate, MONTHLY_PLAN } from \"../shopify.server\";\n *\n * export const loader = async ({ request }: LoaderFunctionArgs) => {\n * const { billing } = await authenticate.admin(request);\n * await billing.require({\n * plans: [MONTHLY_PLAN],\n * isTest: true,\n * onFailure: async () => billing.request({ plan: MONTHLY_PLAN }),\n * });\n *\n * // App logic\n * };\n * ```\n * ```ts\n * // shopify.server.ts\n * import { shopifyApp, BillingInterval } from \"@shopify/shopify-app-remix/server\";\n *\n * export const MONTHLY_PLAN = 'Monthly subscription';\n * export const ANNUAL_PLAN = 'Annual subscription';\n *\n * const shopify = shopifyApp({\n * // ...etc\n * billing: {\n * [MONTHLY_PLAN]: {\n * lineItems: [\n * {\n * amount: 5,\n * currencyCode: 'USD',\n * interval: BillingInterval.Every30Days,\n * },\n * {\n * amount: 1,\n * currencyCode: 'USD',\n * interval: BillingInterval.Usage.\n * terms: '1 dollar per 1000 emails',\n * },\n * ],\n * },\n * }\n * future: {v3_lineItemBilling: true}\n * });\n * export default shopify;\n * export const authenticate = shopify.authenticate;\n * ```\n */\n require: (\n options: RequireBillingOptions,\n ) => Promise;\n\n /**\n * Checks if the shop has an active payment for any plan defined in the `billing` config option.\n *\n * @returns A promise that resolves to an object containing the active purchases for the shop.\n *\n * @example\n * Check what billing plans a merchant is subscribed to.\n * Use billing.check if you want to determine which plans are in use. Unlike `require`, `check` does not\n * throw an error if no active billing plans are present. \n * ```ts\n * // /app/routes/**\\/*.ts\n * import { LoaderFunctionArgs } from \"@remix-run/node\";\n * import { authenticate, MONTHLY_PLAN } from \"../shopify.server\";\n *\n * export const loader = async ({ request }: LoaderFunctionArgs) => {\n * const { billing } = await authenticate.admin(request);\n * const { hasActivePayment, appSubscriptions } = await billing.check({\n * plans: [MONTHLY_PLAN],\n * isTest: false,\n * });\n * console.log(hasActivePayment)\n * console.log(appSubscriptions)\n * };\n * ```\n * ```ts\n * // shopify.server.ts\n * import { shopifyApp, BillingInterval } from \"@shopify/shopify-app-remix/server\";\n *\n * export const MONTHLY_PLAN = 'Monthly subscription';\n * export const ANNUAL_PLAN = 'Annual subscription';\n *\n * const shopify = shopifyApp({\n * // ...etc\n * billing: {\n * [MONTHLY_PLAN]: {\n * amount: 5,\n * currencyCode: 'USD',\n * interval: BillingInterval.Every30Days,\n * },\n * [ANNUAL_PLAN]: {\n * amount: 50,\n * currencyCode: 'USD',\n * interval: BillingInterval.Annual,\n * },\n * }\n * });\n * export default shopify;\n * export const authenticate = shopify.authenticate;\n * ```\n *\n */\n check: (\n options: CheckBillingOptions,\n ) => Promise;\n\n /**\n * Requests payment for the plan.\n *\n * @returns Redirects to the confirmation URL for the payment.\n *\n * @example\n * Using a custom return URL.\n * Change where the merchant is returned to after approving the purchase using the `returnUrl` option.\n * ```ts\n * // /app/routes/**\\/*.ts\n * import { LoaderFunctionArgs } from \"@remix-run/node\";\n * import { authenticate, MONTHLY_PLAN } from \"../shopify.server\";\n *\n * export const loader = async ({ request }: LoaderFunctionArgs) => {\n * const { billing } = await authenticate.admin(request);\n * await billing.require({\n * plans: [MONTHLY_PLAN],\n * onFailure: async () => billing.request({\n * plan: MONTHLY_PLAN,\n * isTest: true,\n * returnUrl: 'https://admin.shopify.com/store/my-store/apps/my-app/billing-page',\n * }),\n * });\n *\n * // App logic\n * };\n * ```\n * ```ts\n * // shopify.server.ts\n * import { shopifyApp, BillingInterval } from \"@shopify/shopify-app-remix/server\";\n *\n * export const MONTHLY_PLAN = 'Monthly subscription';\n * export const ANNUAL_PLAN = 'Annual subscription';\n *\n * const shopify = shopifyApp({\n * // ...etc\n * billing: {\n * [MONTHLY_PLAN]: {\n * amount: 5,\n * currencyCode: 'USD',\n * interval: BillingInterval.Every30Days,\n * },\n * [ANNUAL_PLAN]: {\n * amount: 50,\n * currencyCode: 'USD',\n * interval: BillingInterval.Annual,\n * },\n * }\n * });\n * export default shopify;\n * export const authenticate = shopify.authenticate;\n * ```\n */\n request: (options: RequestBillingOptions) => Promise;\n\n /**\n * Cancels an ongoing subscription, given its ID.\n *\n * @returns The cancelled subscription.\n *\n * @example\n * Cancelling a subscription.\n * Use the `billing.cancel` function to cancel an active subscription with the id returned from `billing.require`.\n * ```ts\n * // /app/routes/cancel-subscription.ts\n * import { LoaderFunctionArgs } from \"@remix-run/node\";\n * import { authenticate, MONTHLY_PLAN } from \"../shopify.server\";\n *\n * export const loader = async ({ request }: LoaderFunctionArgs) => {\n * const { billing } = await authenticate.admin(request);\n * const billingCheck = await billing.require({\n * plans: [MONTHLY_PLAN],\n * onFailure: async () => billing.request({ plan: MONTHLY_PLAN }),\n * });\n *\n * const subscription = billingCheck.appSubscriptions[0];\n * const cancelledSubscription = await billing.cancel({\n * subscriptionId: subscription.id,\n * isTest: true,\n * prorate: true,\n * });\n *\n * // App logic\n * };\n * ```\n * ```ts\n * // shopify.server.ts\n * import { shopifyApp, BillingInterval } from \"@shopify/shopify-app-remix/server\";\n *\n * export const MONTHLY_PLAN = 'Monthly subscription';\n * export const ANNUAL_PLAN = 'Annual subscription';\n *\n * const shopify = shopifyApp({\n * // ...etc\n * billing: {\n * [MONTHLY_PLAN]: {\n * amount: 5,\n * currencyCode: 'USD',\n * interval: BillingInterval.Every30Days,\n * },\n * [ANNUAL_PLAN]: {\n * amount: 50,\n * currencyCode: 'USD',\n * interval: BillingInterval.Annual,\n * },\n * }\n * });\n * export default shopify;\n * export const authenticate = shopify.authenticate;\n * ```\n */\n cancel: (options: CancelBillingOptions) => Promise;\n}" }, "RequireBillingOptions": { "filePath": "src/server/authenticate/admin/billing/types.ts", @@ -1136,12 +1260,12 @@ "description": "Get user-specific data using the `sessionToken` object.", "tabs": [ { - "code": "import { shopifyApp } from \"@shopify/shopify-app-remix/server\";\n\nconst shopify = shopifyApp({\n // ...etc\n useOnlineTokens: true,\n});\nexport default shopify;\nexport const authenticate = shopify.authenticate;", - "title": "shopify.server.ts" + "code": "import { LoaderFunctionArgs, json } from \"@remix-run/node\";\nimport { authenticate } from \"../shopify.server\";\nimport { getMyAppData } from \"~/db/model.server\";\n\nexport const loader = async ({ request }: LoaderFunctionArgs) => {\n const { sessionToken } = await authenticate.admin(\n request\n );\n return json(await getMyAppData({user: sessionToken.sub}));\n};", + "title": "/app/routes/**\\/*.ts" }, { - "code": "import { LoaderFunctionArgs, json } from \"@remix-run/node\";\nimport { authenticate } from \"../shopify.server\";\nimport { getMyAppData } from \"~/db/model.server\";\n\nexport const loader = async ({ request }: LoaderFunctionArgs) => {\n const { sessionToken } = await authenticate.public.checkout(\n request\n );\n return json(await getMyAppData({user: sessionToken.sub}));\n};", - "title": "/app/routes/**\\/*.ts" + "code": "import { shopifyApp } from \"@shopify/shopify-app-remix/server\";\n\nconst shopify = shopifyApp({\n // ...etc\n useOnlineTokens: true,\n});\nexport default shopify;\nexport const authenticate = shopify.authenticate;", + "title": "shopify.server.ts" } ] } @@ -1187,13 +1311,13 @@ "title": "Using offline sessions", "description": "Get your app's shop-specific data using an offline session.", "tabs": [ - { - "code": "import { shopifyApp } from \"@shopify/shopify-app-remix/server\";\n\nconst shopify = shopifyApp({\n // ...etc\n});\nexport default shopify;\nexport const authenticate = shopify.authenticate;", - "title": "shopify.server.ts" - }, { "code": "import { LoaderFunctionArgs, json } from \"@remix-run/node\";\nimport { authenticate } from \"../shopify.server\";\nimport { getMyAppData } from \"~/db/model.server\";\n\nexport const loader = async ({ request }: LoaderFunctionArgs) => {\n const { session } = await authenticate.admin(request);\n return json(await getMyAppData({shop: session.shop));\n};", "title": "/app/routes/**\\/*.ts" + }, + { + "code": "import { shopifyApp } from \"@shopify/shopify-app-remix/server\";\n\nconst shopify = shopifyApp({\n // ...etc\n});\nexport default shopify;\nexport const authenticate = shopify.authenticate;", + "title": "shopify.server.ts" } ] }, @@ -1201,13 +1325,13 @@ "title": "Using online sessions", "description": "Get your app's user-specific data using an online session.", "tabs": [ - { - "code": "import { shopifyApp } from \"@shopify/shopify-app-remix/server\";\n\nconst shopify = shopifyApp({\n // ...etc\n useOnlineTokens: true,\n});\nexport default shopify;\nexport const authenticate = shopify.authenticate;", - "title": "shopify.server.ts" - }, { "code": "import { LoaderFunctionArgs, json } from \"@remix-run/node\";\nimport { authenticate } from \"../shopify.server\";\nimport { getMyAppData } from \"~/db/model.server\";\n\nexport const loader = async ({ request }: LoaderFunctionArgs) => {\n const { session } = await authenticate.admin(request);\n return json(await getMyAppData({user: session.onlineAccessInfo!.id}));\n};", "title": "/app/routes/**\\/*.ts" + }, + { + "code": "import { shopifyApp } from \"@shopify/shopify-app-remix/server\";\n\nconst shopify = shopifyApp({\n // ...etc\n useOnlineTokens: true,\n});\nexport default shopify;\nexport const authenticate = shopify.authenticate;", + "title": "shopify.server.ts" } ] } @@ -1247,7 +1371,7 @@ ] } ], - "value": "export interface EmbeddedAdminContext<\n Config extends AppConfigArg,\n Resources extends ShopifyRestResources = ShopifyRestResources,\n> extends AdminContextInternal {\n /**\n * The decoded and validated session token for the request.\n *\n * Returned only if `isEmbeddedApp` is `true`.\n *\n * {@link https://shopify.dev/docs/apps/auth/oauth/session-tokens#payload}\n *\n * @example\n * Using the decoded session token.\n * Get user-specific data using the `sessionToken` object.\n * ```ts\n * // shopify.server.ts\n * import { shopifyApp } from \"@shopify/shopify-app-remix/server\";\n *\n * const shopify = shopifyApp({\n * // ...etc\n * useOnlineTokens: true,\n * });\n * export default shopify;\n * export const authenticate = shopify.authenticate;\n * ```\n * ```ts\n * // /app/routes/**\\/*.ts\n * import { LoaderFunctionArgs, json } from \"@remix-run/node\";\n * import { authenticate } from \"../shopify.server\";\n * import { getMyAppData } from \"~/db/model.server\";\n *\n * export const loader = async ({ request }: LoaderFunctionArgs) => {\n * const { sessionToken } = await authenticate.public.checkout(\n * request\n * );\n * return json(await getMyAppData({user: sessionToken.sub}));\n * };\n * ```\n */\n sessionToken: JwtPayload;\n\n /**\n * A function that redirects the user to a new page, ensuring that the appropriate parameters are set for embedded\n * apps.\n *\n * Returned only if `isEmbeddedApp` is `true`.\n *\n * @example\n * Redirecting to an app route.\n * Use the `redirect` helper to safely redirect between pages.\n * ```ts\n * // /app/routes/admin/my-route.ts\n * import { LoaderFunctionArgs, json } from \"@remix-run/node\";\n * import { authenticate } from \"../shopify.server\";\n *\n * export const loader = async ({ request }: LoaderFunctionArgs) => {\n * const { session, redirect } = await authenticate.admin(request);\n * return redirect(\"/\");\n * };\n * ```\n *\n * @example\n * Redirecting outside of Shopify admin.\n * Pass in a `target` option of `_top` or `_parent` to go to an external URL.\n * ```ts\n * // /app/routes/admin/my-route.ts\n * import { LoaderFunctionArgs, json } from \"@remix-run/node\";\n * import { authenticate } from \"../shopify.server\";\n *\n * export const loader = async ({ request }: LoaderFunctionArgs) => {\n * const { session, redirect } = await authenticate.admin(request);\n * return redirect(\"/\", { target: '_parent' });\n * };\n * ```\n */\n redirect: RedirectFunction;\n}" + "value": "export interface EmbeddedAdminContext<\n Config extends AppConfigArg,\n Resources extends ShopifyRestResources = ShopifyRestResources,\n> extends AdminContextInternal {\n /**\n * The decoded and validated session token for the request.\n *\n * Returned only if `isEmbeddedApp` is `true`.\n *\n * {@link https://shopify.dev/docs/apps/auth/oauth/session-tokens#payload}\n *\n * @example\n * Using the decoded session token.\n * Get user-specific data using the `sessionToken` object.\n * ```ts\n * // /app/routes/**\\/*.ts\n * import { LoaderFunctionArgs, json } from \"@remix-run/node\";\n * import { authenticate } from \"../shopify.server\";\n * import { getMyAppData } from \"~/db/model.server\";\n *\n * export const loader = async ({ request }: LoaderFunctionArgs) => {\n * const { sessionToken } = await authenticate.admin(\n * request\n * );\n * return json(await getMyAppData({user: sessionToken.sub}));\n * };\n * ```\n * ```ts\n * // shopify.server.ts\n * import { shopifyApp } from \"@shopify/shopify-app-remix/server\";\n *\n * const shopify = shopifyApp({\n * // ...etc\n * useOnlineTokens: true,\n * });\n * export default shopify;\n * export const authenticate = shopify.authenticate;\n * ```\n */\n sessionToken: JwtPayload;\n\n /**\n * A function that redirects the user to a new page, ensuring that the appropriate parameters are set for embedded\n * apps.\n *\n * Returned only if `isEmbeddedApp` is `true`.\n *\n * @example\n * Redirecting to an app route.\n * Use the `redirect` helper to safely redirect between pages.\n * ```ts\n * // /app/routes/admin/my-route.ts\n * import { LoaderFunctionArgs, json } from \"@remix-run/node\";\n * import { authenticate } from \"../shopify.server\";\n *\n * export const loader = async ({ request }: LoaderFunctionArgs) => {\n * const { session, redirect } = await authenticate.admin(request);\n * return redirect(\"/\");\n * };\n * ```\n *\n * @example\n * Redirecting outside of Shopify admin.\n * Pass in a `target` option of `_top` or `_parent` to go to an external URL.\n * ```ts\n * // /app/routes/admin/my-route.ts\n * import { LoaderFunctionArgs, json } from \"@remix-run/node\";\n * import { authenticate } from \"../shopify.server\";\n *\n * export const loader = async ({ request }: LoaderFunctionArgs) => {\n * const { session, redirect } = await authenticate.admin(request);\n * return redirect(\"/\", { target: '_parent' });\n * };\n * ```\n */\n redirect: RedirectFunction;\n}" }, "JwtPayload": { "filePath": "../../node_modules/@shopify/shopify-api/lib/session/types.d.ts", @@ -1393,13 +1517,13 @@ "title": "Using the decoded session token", "tabs": [ { - "title": "shopify.server.ts", - "code": "import { shopifyApp } from \"@shopify/shopify-app-remix/server\";\n\nconst shopify = shopifyApp({\n // ...etc\n useOnlineTokens: true,\n});\nexport default shopify;\nexport const authenticate = shopify.authenticate;", + "title": "/app/routes/**\\/*.ts", + "code": "import { LoaderFunctionArgs, json } from \"@remix-run/node\";\nimport { authenticate } from \"../shopify.server\";\nimport { getMyAppData } from \"~/db/model.server\";\n\nexport const loader = async ({ request }: LoaderFunctionArgs) => {\n const { sessionToken } = await authenticate.admin(\n request\n );\n return json(await getMyAppData({user: sessionToken.sub}));\n};", "language": "typescript" }, { - "title": "/app/routes/**\\/*.ts", - "code": "import { LoaderFunctionArgs, json } from \"@remix-run/node\";\nimport { authenticate } from \"../shopify.server\";\nimport { getMyAppData } from \"~/db/model.server\";\n\nexport const loader = async ({ request }: LoaderFunctionArgs) => {\n const { sessionToken } = await authenticate.public.checkout(\n request\n );\n return json(await getMyAppData({user: sessionToken.sub}));\n};", + "title": "shopify.server.ts", + "code": "import { shopifyApp } from \"@shopify/shopify-app-remix/server\";\n\nconst shopify = shopifyApp({\n // ...etc\n useOnlineTokens: true,\n});\nexport default shopify;\nexport const authenticate = shopify.authenticate;", "language": "typescript" } ] @@ -1447,13 +1571,13 @@ "title": "Using offline sessions", "tabs": [ { - "title": "shopify.server.ts", - "code": "import { shopifyApp } from \"@shopify/shopify-app-remix/server\";\n\nconst shopify = shopifyApp({\n // ...etc\n});\nexport default shopify;\nexport const authenticate = shopify.authenticate;", + "title": "/app/routes/**\\/*.ts", + "code": "import { LoaderFunctionArgs, json } from \"@remix-run/node\";\nimport { authenticate } from \"../shopify.server\";\nimport { getMyAppData } from \"~/db/model.server\";\n\nexport const loader = async ({ request }: LoaderFunctionArgs) => {\n const { session } = await authenticate.admin(request);\n return json(await getMyAppData({shop: session.shop));\n};", "language": "typescript" }, { - "title": "/app/routes/**\\/*.ts", - "code": "import { LoaderFunctionArgs, json } from \"@remix-run/node\";\nimport { authenticate } from \"../shopify.server\";\nimport { getMyAppData } from \"~/db/model.server\";\n\nexport const loader = async ({ request }: LoaderFunctionArgs) => {\n const { session } = await authenticate.admin(request);\n return json(await getMyAppData({shop: session.shop));\n};", + "title": "shopify.server.ts", + "code": "import { shopifyApp } from \"@shopify/shopify-app-remix/server\";\n\nconst shopify = shopifyApp({\n // ...etc\n});\nexport default shopify;\nexport const authenticate = shopify.authenticate;", "language": "typescript" } ] @@ -1465,13 +1589,13 @@ "title": "Using online sessions", "tabs": [ { - "title": "shopify.server.ts", - "code": "import { shopifyApp } from \"@shopify/shopify-app-remix/server\";\n\nconst shopify = shopifyApp({\n // ...etc\n useOnlineTokens: true,\n});\nexport default shopify;\nexport const authenticate = shopify.authenticate;", + "title": "/app/routes/**\\/*.ts", + "code": "import { LoaderFunctionArgs, json } from \"@remix-run/node\";\nimport { authenticate } from \"../shopify.server\";\nimport { getMyAppData } from \"~/db/model.server\";\n\nexport const loader = async ({ request }: LoaderFunctionArgs) => {\n const { session } = await authenticate.admin(request);\n return json(await getMyAppData({user: session.onlineAccessInfo!.id}));\n};", "language": "typescript" }, { - "title": "/app/routes/**\\/*.ts", - "code": "import { LoaderFunctionArgs, json } from \"@remix-run/node\";\nimport { authenticate } from \"../shopify.server\";\nimport { getMyAppData } from \"~/db/model.server\";\n\nexport const loader = async ({ request }: LoaderFunctionArgs) => {\n const { session } = await authenticate.admin(request);\n return json(await getMyAppData({user: session.onlineAccessInfo!.id}));\n};", + "title": "shopify.server.ts", + "code": "import { shopifyApp } from \"@shopify/shopify-app-remix/server\";\n\nconst shopify = shopifyApp({\n // ...etc\n useOnlineTokens: true,\n});\nexport default shopify;\nexport const authenticate = shopify.authenticate;", "language": "typescript" } ] @@ -1507,7 +1631,7 @@ "tabs": [ { "title": "/app/routes/**\\/*.ts", - "code": "import { LoaderFunctionArgs, json } from \"@remix-run/node\";\nimport { authenticate } from \"../shopify.server\";\n\nexport const loader = async ({ request }: LoaderFunctionArgs) => {\n const { admin, session } = await authenticate.admin(request);\n return json(admin.rest.resources.Order.count({ session }));\n};", + "code": "import { LoaderFunctionArgs, json } from \"@remix-run/node\";\nimport { authenticate } from \"../shopify.server\";\n\nexport const loader = async ({ request }: LoaderFunctionArgs) => {\n const {\n admin,\n session,\n } = await authenticate.admin(request);\n\n return json(\n admin.rest.resources.Order.count({ session }),\n );\n};", "language": "typescript" }, { @@ -1525,7 +1649,7 @@ "tabs": [ { "title": "/app/routes/**\\/*.ts", - "code": "import { LoaderFunctionArgs, json } from \"@remix-run/node\";\nimport { authenticate } from \"../shopify.server\";\n\nexport const loader = async ({ request }: LoaderFunctionArgs) => {\n const { admin, session } = await authenticate.admin(request);\n const response = await admin.rest.get({ path: \"/customers/count.json\" });\n const customers = await response.json();\n return json({ customers });\n};", + "code": "import { LoaderFunctionArgs, json } from \"@remix-run/node\";\nimport { authenticate } from \"../shopify.server\";\n\nexport const loader = async ({ request }: LoaderFunctionArgs) => {\n const {\n admin,\n session,\n } = await authenticate.admin(request);\n\n const response = await admin.rest.get({\n path: \"/customers/count.json\",\n });\n const customers = await response.json();\n\n return json({ customers });\n};", "language": "typescript" }, { @@ -1543,7 +1667,7 @@ "tabs": [ { "title": "/app/routes/**\\/*.ts", - "code": "import { LoaderFunctionArgs, json } from \"@remix-run/node\";\nimport { authenticate } from \"../shopify.server\";\n\nexport const loader = async ({ request }: LoaderFunctionArgs) => {\n const { admin, session } = await authenticate.admin(request);\n const response = admin.rest.post({\n path: \"customers/7392136888625/send_invite.json\",\n body: {\n customer_invite: {\n to: \"new_test_email@shopify.com\",\n from: \"j.limited@example.com\",\n bcc: [\"j.limited@example.com\"],\n subject: \"Welcome to my new shop\",\n custom_message: \"My awesome new store\",\n },\n },\n});\n const customerInvite = await response.json();\n return json({ customerInvite });\n};", + "code": "import { LoaderFunctionArgs, json } from \"@remix-run/node\";\nimport { authenticate } from \"../shopify.server\";\n\nexport const loader = async ({ request }: LoaderFunctionArgs) => {\n const {\n admin,\n session,\n } = await authenticate.admin(request);\n\n const response = admin.rest.post({\n path: \"customers/7392136888625/send_invite.json\",\n body: {\n customer_invite: {\n to: \"new_test_email@shopify.com\",\n from: \"j.limited@example.com\",\n bcc: [\"j.limited@example.com\"],\n subject: \"Welcome to my new shop\",\n custom_message: \"My awesome new store\",\n },\n },\n });\n\n const customerInvite = await response.json();\n return json({ customerInvite });\n};", "language": "typescript" }, { @@ -1565,13 +1689,31 @@ "title": "Querying the GraphQL API", "tabs": [ { - "title": "Example", - "code": "import { ActionFunctionArgs } from \"@remix-run/node\";\nimport { authenticate } from \"../shopify.server\";\n\nexport async function action({ request }: ActionFunctionArgs) {\n const { admin } = await authenticate.admin(request);\n\n const response = await admin.graphql(\n `#graphql\n mutation populateProduct($input: ProductInput!) {\n productCreate(input: $input) {\n product {\n id\n }\n }\n }`,\n { variables: { input: { title: \"Product Name\" } } }\n );\n\n const productData = await response.json();\n return json({ data: productData.data });\n}", + "title": "/app/routes/**\\/*.ts", + "code": "import { ActionFunctionArgs } from \"@remix-run/node\";\nimport { authenticate } from \"../shopify.server\";\n\nexport const action = async ({ request }: ActionFunctionArgs) => {\n const { admin } = await authenticate.admin(request);\n\n const response = await admin.graphql(\n `#graphql\n mutation populateProduct($input: ProductInput!) {\n productCreate(input: $input) {\n product {\n id\n }\n }\n }`,\n {\n variables: {\n input: { title: \"Product Name\" },\n },\n },\n );\n\n const productData = await response.json();\n return json({\n productId: productData.data?.productCreate?.product?.id,\n });\n}", + "language": "typescript" + }, + { + "title": "/app/shopify.server.ts", + "code": "import { shopifyApp } from \"@shopify/shopify-app-remix/server\";\n\nconst shopify = shopifyApp({\n // ...\n});\nexport default shopify;\nexport const authenticate = shopify.authenticate;", + "language": "typescript" + } + ] + } + }, + { + "description": "Catch `GraphqlQueryError` errors to see error messages from the API.", + "codeblock": { + "title": "Handling GraphQL errors", + "tabs": [ + { + "title": "/app/routes/**\\/*.ts", + "code": "import { ActionFunctionArgs } from \"@remix-run/node\";\nimport { authenticate } from \"../shopify.server\";\n\nexport const action = async ({ request }: ActionFunctionArgs) => {\n const { admin } = await authenticate.admin(request);\n\n try {\n const response = await admin.graphql(\n `#graphql\n query incorrectQuery {\n products(first: 10) {\n nodes {\n not_a_field\n }\n }\n }`,\n );\n\n return json({ data: await response.json() });\n } catch (error) {\n if (error instanceof GraphqlQueryError) {\n // error.body.errors:\n // { graphQLErrors: [\n // { message: \"Field 'not_a_field' doesn't exist on type 'Product'\" }\n // ] }\n return json({ errors: error.body?.errors }, { status: 500 });\n }\n return json({ message: \"An error occurred\" }, { status: 500 });\n }\n}", "language": "typescript" }, { "title": "/app/shopify.server.ts", - "code": "import { shopifyApp } from \"@shopify/shopify-app-remix/server\";\n\nconst shopify = shopifyApp({\n restResources,\n // ...etc\n});\nexport default shopify;\nexport const unauthenticated = shopify.unauthenticated;", + "code": "import { shopifyApp } from \"@shopify/shopify-app-remix/server\";\n\nconst shopify = shopifyApp({\n // ...\n});\nexport default shopify;\nexport const authenticate = shopify.authenticate;", "language": "typescript" } ] @@ -1630,7 +1772,7 @@ }, { "title": "shopify.server.ts", - "code": "import { shopifyApp, BillingInterval } from \"@shopify/shopify-app-remix/server\";\n\nexport const MONTHLY_PLAN = 'Monthly subscription';\nexport const ANNUAL_PLAN = 'Annual subscription';\n\nconst shopify = shopifyApp({\n // ...etc\n billing: {\n [MONTHLY_PLAN]: {\n lineItems: [\n {\n amount: 5,\n currencyCode: 'USD',\n interval: BillingInterval.Every30Days,\n }\n {\n amount: 1,\n currencyCode: 'USD',\n interval: BillingInterval.Usage.\n terms: '1 dollar per 1000 emails',\n },\n ],\n },\n }\n future: {v3_lineItemBilling: true}\n});\nexport default shopify;\nexport const authenticate = shopify.authenticate;", + "code": "import { shopifyApp, BillingInterval } from \"@shopify/shopify-app-remix/server\";\n\nexport const MONTHLY_PLAN = 'Monthly subscription';\nexport const ANNUAL_PLAN = 'Annual subscription';\n\nconst shopify = shopifyApp({\n // ...etc\n billing: {\n [MONTHLY_PLAN]: {\n lineItems: [\n {\n amount: 5,\n currencyCode: 'USD',\n interval: BillingInterval.Every30Days,\n },\n {\n amount: 1,\n currencyCode: 'USD',\n interval: BillingInterval.Usage.\n terms: '1 dollar per 1000 emails',\n },\n ],\n },\n }\n future: {v3_lineItemBilling: true}\n});\nexport default shopify;\nexport const authenticate = shopify.authenticate;", "language": "typescript" } ] @@ -1771,7 +1913,7 @@ "title": "/app/routes/**\\/*.ts" }, { - "code": "import { shopifyApp, BillingInterval } from \"@shopify/shopify-app-remix/server\";\n\nexport const MONTHLY_PLAN = 'Monthly subscription';\nexport const ANNUAL_PLAN = 'Annual subscription';\n\nconst shopify = shopifyApp({\n // ...etc\n billing: {\n [MONTHLY_PLAN]: {\n lineItems: [\n {\n amount: 5,\n currencyCode: 'USD',\n interval: BillingInterval.Every30Days,\n }\n {\n amount: 1,\n currencyCode: 'USD',\n interval: BillingInterval.Usage.\n terms: '1 dollar per 1000 emails',\n },\n ],\n },\n }\n future: {v3_lineItemBilling: true}\n});\nexport default shopify;\nexport const authenticate = shopify.authenticate;", + "code": "import { shopifyApp, BillingInterval } from \"@shopify/shopify-app-remix/server\";\n\nexport const MONTHLY_PLAN = 'Monthly subscription';\nexport const ANNUAL_PLAN = 'Annual subscription';\n\nconst shopify = shopifyApp({\n // ...etc\n billing: {\n [MONTHLY_PLAN]: {\n lineItems: [\n {\n amount: 5,\n currencyCode: 'USD',\n interval: BillingInterval.Every30Days,\n },\n {\n amount: 1,\n currencyCode: 'USD',\n interval: BillingInterval.Usage.\n terms: '1 dollar per 1000 emails',\n },\n ],\n },\n }\n future: {v3_lineItemBilling: true}\n});\nexport default shopify;\nexport const authenticate = shopify.authenticate;", "title": "shopify.server.ts" } ] @@ -1848,7 +1990,7 @@ ] } ], - "value": "export interface BillingContext {\n /**\n * Checks if the shop has an active payment for any plan defined in the `billing` config option.\n *\n * @returns A promise that resolves to an object containing the active purchases for the shop.\n *\n * @example\n * Requesting billing right away.\n * Call `billing.request` in the `onFailure` callback to immediately redirect to the Shopify page to request payment.\n * ```ts\n * // /app/routes/**\\/*.ts\n * import { LoaderFunctionArgs } from \"@remix-run/node\";\n * import { authenticate, MONTHLY_PLAN } from \"../shopify.server\";\n *\n * export const loader = async ({ request }: LoaderFunctionArgs) => {\n * const { billing } = await authenticate.admin(request);\n * await billing.require({\n * plans: [MONTHLY_PLAN],\n * isTest: true,\n * onFailure: async () => billing.request({ plan: MONTHLY_PLAN }),\n * });\n *\n * // App logic\n * };\n * ```\n * ```ts\n * // shopify.server.ts\n * import { shopifyApp, BillingInterval } from \"@shopify/shopify-app-remix/server\";\n *\n * export const MONTHLY_PLAN = 'Monthly subscription';\n * export const ANNUAL_PLAN = 'Annual subscription';\n *\n * const shopify = shopifyApp({\n * // ...etc\n * billing: {\n * [MONTHLY_PLAN]: {\n * amount: 5,\n * currencyCode: 'USD',\n * interval: BillingInterval.Every30Days,\n * },\n * [ANNUAL_PLAN]: {\n * amount: 50,\n * currencyCode: 'USD',\n * interval: BillingInterval.Annual,\n * },\n * }\n * });\n * export default shopify;\n * export const authenticate = shopify.authenticate;\n * ```\n *\n * @example\n * Redirect to a plan selection page.\n * When the app has multiple plans, create a page in your App that allows the merchant to select a plan. If a merchant does not have the required plan you can redirect them to page in your app to select one.\n * ```ts\n * // /app/routes/**\\/*.ts\n * import { LoaderFunctionArgs, redirect } from \"@remix-run/node\";\n * import { authenticate, MONTHLY_PLAN, ANNUAL_PLAN } from \"../shopify.server\";\n *\n * export const loader = async ({ request }: LoaderFunctionArgs) => {\n * const { billing } = await authenticate.admin(request);\n * const billingCheck = await billing.require({\n * plans: [MONTHLY_PLAN, ANNUAL_PLAN],\n * isTest: true,\n * onFailure: () => redirect('/select-plan'),\n * });\n *\n * const subscription = billingCheck.appSubscriptions[0];\n * console.log(`Shop is on ${subscription.name} (id ${subscription.id})`);\n *\n * // App logic\n * };\n * ```\n * ```ts\n * // shopify.server.ts\n * import { shopifyApp, BillingInterval } from \"@shopify/shopify-app-remix/server\";\n *\n * export const MONTHLY_PLAN = 'Monthly subscription';\n * export const ANNUAL_PLAN = 'Annual subscription';\n *\n * const shopify = shopifyApp({\n * // ...etc\n * billing: {\n * [MONTHLY_PLAN]: {\n * amount: 5,\n * currencyCode: 'USD',\n * interval: BillingInterval.Every30Days,\n * },\n * [ANNUAL_PLAN]: {\n * amount: 50,\n * currencyCode: 'USD',\n * interval: BillingInterval.Annual,\n * },\n * }\n * });\n * export default shopify;\n * export const authenticate = shopify.authenticate;\n * ```\n * @example\n * Requesting billing with line items\n * Call `billing.request` with the `v3_lineItemBilling` future flag enabled\n * ```ts\n * // /app/routes/**\\/*.ts\n * import { LoaderFunctionArgs } from \"@remix-run/node\";\n * import { authenticate, MONTHLY_PLAN } from \"../shopify.server\";\n *\n * export const loader = async ({ request }: LoaderFunctionArgs) => {\n * const { billing } = await authenticate.admin(request);\n * await billing.require({\n * plans: [MONTHLY_PLAN],\n * isTest: true,\n * onFailure: async () => billing.request({ plan: MONTHLY_PLAN }),\n * });\n *\n * // App logic\n * };\n * ```\n * ```ts\n * // shopify.server.ts\n * import { shopifyApp, BillingInterval } from \"@shopify/shopify-app-remix/server\";\n *\n * export const MONTHLY_PLAN = 'Monthly subscription';\n * export const ANNUAL_PLAN = 'Annual subscription';\n *\n * const shopify = shopifyApp({\n * // ...etc\n * billing: {\n * [MONTHLY_PLAN]: {\n * lineItems: [\n * {\n * amount: 5,\n * currencyCode: 'USD',\n * interval: BillingInterval.Every30Days,\n * }\n * {\n * amount: 1,\n * currencyCode: 'USD',\n * interval: BillingInterval.Usage.\n * terms: '1 dollar per 1000 emails',\n * },\n * ],\n * },\n * }\n * future: {v3_lineItemBilling: true}\n * });\n * export default shopify;\n * export const authenticate = shopify.authenticate;\n * ```\n */\n require: (\n options: RequireBillingOptions,\n ) => Promise;\n\n /**\n * Checks if the shop has an active payment for any plan defined in the `billing` config option.\n *\n * @returns A promise that resolves to an object containing the active purchases for the shop.\n *\n * @example\n * Check what billing plans a merchant is subscribed to.\n * Use billing.check if you want to determine which plans are in use. Unlike `require`, `check` does not\n * throw an error if no active billing plans are present. \n * ```ts\n * // /app/routes/**\\/*.ts\n * import { LoaderFunctionArgs } from \"@remix-run/node\";\n * import { authenticate, MONTHLY_PLAN } from \"../shopify.server\";\n *\n * export const loader = async ({ request }: LoaderFunctionArgs) => {\n * const { billing } = await authenticate.admin(request);\n * const { hasActivePayment, appSubscriptions } = await billing.check({\n * plans: [MONTHLY_PLAN],\n * isTest: false,\n * });\n * console.log(hasActivePayment)\n * console.log(appSubscriptions)\n * };\n * ```\n * ```ts\n * // shopify.server.ts\n * import { shopifyApp, BillingInterval } from \"@shopify/shopify-app-remix/server\";\n *\n * export const MONTHLY_PLAN = 'Monthly subscription';\n * export const ANNUAL_PLAN = 'Annual subscription';\n *\n * const shopify = shopifyApp({\n * // ...etc\n * billing: {\n * [MONTHLY_PLAN]: {\n * amount: 5,\n * currencyCode: 'USD',\n * interval: BillingInterval.Every30Days,\n * },\n * [ANNUAL_PLAN]: {\n * amount: 50,\n * currencyCode: 'USD',\n * interval: BillingInterval.Annual,\n * },\n * }\n * });\n * export default shopify;\n * export const authenticate = shopify.authenticate;\n * ```\n *\n */\n check: (\n options: CheckBillingOptions,\n ) => Promise;\n\n /**\n * Requests payment for the plan.\n *\n * @returns Redirects to the confirmation URL for the payment.\n *\n * @example\n * Using a custom return URL.\n * Change where the merchant is returned to after approving the purchase using the `returnUrl` option.\n * ```ts\n * // /app/routes/**\\/*.ts\n * import { LoaderFunctionArgs } from \"@remix-run/node\";\n * import { authenticate, MONTHLY_PLAN } from \"../shopify.server\";\n *\n * export const loader = async ({ request }: LoaderFunctionArgs) => {\n * const { billing } = await authenticate.admin(request);\n * await billing.require({\n * plans: [MONTHLY_PLAN],\n * onFailure: async () => billing.request({\n * plan: MONTHLY_PLAN,\n * isTest: true,\n * returnUrl: 'https://admin.shopify.com/store/my-store/apps/my-app/billing-page',\n * }),\n * });\n *\n * // App logic\n * };\n * ```\n * ```ts\n * // shopify.server.ts\n * import { shopifyApp, BillingInterval } from \"@shopify/shopify-app-remix/server\";\n *\n * export const MONTHLY_PLAN = 'Monthly subscription';\n * export const ANNUAL_PLAN = 'Annual subscription';\n *\n * const shopify = shopifyApp({\n * // ...etc\n * billing: {\n * [MONTHLY_PLAN]: {\n * amount: 5,\n * currencyCode: 'USD',\n * interval: BillingInterval.Every30Days,\n * },\n * [ANNUAL_PLAN]: {\n * amount: 50,\n * currencyCode: 'USD',\n * interval: BillingInterval.Annual,\n * },\n * }\n * });\n * export default shopify;\n * export const authenticate = shopify.authenticate;\n * ```\n */\n request: (options: RequestBillingOptions) => Promise;\n\n /**\n * Cancels an ongoing subscription, given its ID.\n *\n * @returns The cancelled subscription.\n *\n * @example\n * Cancelling a subscription.\n * Use the `billing.cancel` function to cancel an active subscription with the id returned from `billing.require`.\n * ```ts\n * // /app/routes/cancel-subscription.ts\n * import { LoaderFunctionArgs } from \"@remix-run/node\";\n * import { authenticate, MONTHLY_PLAN } from \"../shopify.server\";\n *\n * export const loader = async ({ request }: LoaderFunctionArgs) => {\n * const { billing } = await authenticate.admin(request);\n * const billingCheck = await billing.require({\n * plans: [MONTHLY_PLAN],\n * onFailure: async () => billing.request({ plan: MONTHLY_PLAN }),\n * });\n *\n * const subscription = billingCheck.appSubscriptions[0];\n * const cancelledSubscription = await billing.cancel({\n * subscriptionId: subscription.id,\n * isTest: true,\n * prorate: true,\n * });\n *\n * // App logic\n * };\n * ```\n * ```ts\n * // shopify.server.ts\n * import { shopifyApp, BillingInterval } from \"@shopify/shopify-app-remix/server\";\n *\n * export const MONTHLY_PLAN = 'Monthly subscription';\n * export const ANNUAL_PLAN = 'Annual subscription';\n *\n * const shopify = shopifyApp({\n * // ...etc\n * billing: {\n * [MONTHLY_PLAN]: {\n * amount: 5,\n * currencyCode: 'USD',\n * interval: BillingInterval.Every30Days,\n * },\n * [ANNUAL_PLAN]: {\n * amount: 50,\n * currencyCode: 'USD',\n * interval: BillingInterval.Annual,\n * },\n * }\n * });\n * export default shopify;\n * export const authenticate = shopify.authenticate;\n * ```\n */\n cancel: (options: CancelBillingOptions) => Promise;\n}" + "value": "export interface BillingContext {\n /**\n * Checks if the shop has an active payment for any plan defined in the `billing` config option.\n *\n * @returns A promise that resolves to an object containing the active purchases for the shop.\n *\n * @example\n * Requesting billing right away.\n * Call `billing.request` in the `onFailure` callback to immediately redirect to the Shopify page to request payment.\n * ```ts\n * // /app/routes/**\\/*.ts\n * import { LoaderFunctionArgs } from \"@remix-run/node\";\n * import { authenticate, MONTHLY_PLAN } from \"../shopify.server\";\n *\n * export const loader = async ({ request }: LoaderFunctionArgs) => {\n * const { billing } = await authenticate.admin(request);\n * await billing.require({\n * plans: [MONTHLY_PLAN],\n * isTest: true,\n * onFailure: async () => billing.request({ plan: MONTHLY_PLAN }),\n * });\n *\n * // App logic\n * };\n * ```\n * ```ts\n * // shopify.server.ts\n * import { shopifyApp, BillingInterval } from \"@shopify/shopify-app-remix/server\";\n *\n * export const MONTHLY_PLAN = 'Monthly subscription';\n * export const ANNUAL_PLAN = 'Annual subscription';\n *\n * const shopify = shopifyApp({\n * // ...etc\n * billing: {\n * [MONTHLY_PLAN]: {\n * amount: 5,\n * currencyCode: 'USD',\n * interval: BillingInterval.Every30Days,\n * },\n * [ANNUAL_PLAN]: {\n * amount: 50,\n * currencyCode: 'USD',\n * interval: BillingInterval.Annual,\n * },\n * }\n * });\n * export default shopify;\n * export const authenticate = shopify.authenticate;\n * ```\n *\n * @example\n * Redirect to a plan selection page.\n * When the app has multiple plans, create a page in your App that allows the merchant to select a plan. If a merchant does not have the required plan you can redirect them to page in your app to select one.\n * ```ts\n * // /app/routes/**\\/*.ts\n * import { LoaderFunctionArgs, redirect } from \"@remix-run/node\";\n * import { authenticate, MONTHLY_PLAN, ANNUAL_PLAN } from \"../shopify.server\";\n *\n * export const loader = async ({ request }: LoaderFunctionArgs) => {\n * const { billing } = await authenticate.admin(request);\n * const billingCheck = await billing.require({\n * plans: [MONTHLY_PLAN, ANNUAL_PLAN],\n * isTest: true,\n * onFailure: () => redirect('/select-plan'),\n * });\n *\n * const subscription = billingCheck.appSubscriptions[0];\n * console.log(`Shop is on ${subscription.name} (id ${subscription.id})`);\n *\n * // App logic\n * };\n * ```\n * ```ts\n * // shopify.server.ts\n * import { shopifyApp, BillingInterval } from \"@shopify/shopify-app-remix/server\";\n *\n * export const MONTHLY_PLAN = 'Monthly subscription';\n * export const ANNUAL_PLAN = 'Annual subscription';\n *\n * const shopify = shopifyApp({\n * // ...etc\n * billing: {\n * [MONTHLY_PLAN]: {\n * amount: 5,\n * currencyCode: 'USD',\n * interval: BillingInterval.Every30Days,\n * },\n * [ANNUAL_PLAN]: {\n * amount: 50,\n * currencyCode: 'USD',\n * interval: BillingInterval.Annual,\n * },\n * }\n * });\n * export default shopify;\n * export const authenticate = shopify.authenticate;\n * ```\n * @example\n * Requesting billing with line items\n * Call `billing.request` with the `v3_lineItemBilling` future flag enabled\n * ```ts\n * // /app/routes/**\\/*.ts\n * import { LoaderFunctionArgs } from \"@remix-run/node\";\n * import { authenticate, MONTHLY_PLAN } from \"../shopify.server\";\n *\n * export const loader = async ({ request }: LoaderFunctionArgs) => {\n * const { billing } = await authenticate.admin(request);\n * await billing.require({\n * plans: [MONTHLY_PLAN],\n * isTest: true,\n * onFailure: async () => billing.request({ plan: MONTHLY_PLAN }),\n * });\n *\n * // App logic\n * };\n * ```\n * ```ts\n * // shopify.server.ts\n * import { shopifyApp, BillingInterval } from \"@shopify/shopify-app-remix/server\";\n *\n * export const MONTHLY_PLAN = 'Monthly subscription';\n * export const ANNUAL_PLAN = 'Annual subscription';\n *\n * const shopify = shopifyApp({\n * // ...etc\n * billing: {\n * [MONTHLY_PLAN]: {\n * lineItems: [\n * {\n * amount: 5,\n * currencyCode: 'USD',\n * interval: BillingInterval.Every30Days,\n * },\n * {\n * amount: 1,\n * currencyCode: 'USD',\n * interval: BillingInterval.Usage.\n * terms: '1 dollar per 1000 emails',\n * },\n * ],\n * },\n * }\n * future: {v3_lineItemBilling: true}\n * });\n * export default shopify;\n * export const authenticate = shopify.authenticate;\n * ```\n */\n require: (\n options: RequireBillingOptions,\n ) => Promise;\n\n /**\n * Checks if the shop has an active payment for any plan defined in the `billing` config option.\n *\n * @returns A promise that resolves to an object containing the active purchases for the shop.\n *\n * @example\n * Check what billing plans a merchant is subscribed to.\n * Use billing.check if you want to determine which plans are in use. Unlike `require`, `check` does not\n * throw an error if no active billing plans are present. \n * ```ts\n * // /app/routes/**\\/*.ts\n * import { LoaderFunctionArgs } from \"@remix-run/node\";\n * import { authenticate, MONTHLY_PLAN } from \"../shopify.server\";\n *\n * export const loader = async ({ request }: LoaderFunctionArgs) => {\n * const { billing } = await authenticate.admin(request);\n * const { hasActivePayment, appSubscriptions } = await billing.check({\n * plans: [MONTHLY_PLAN],\n * isTest: false,\n * });\n * console.log(hasActivePayment)\n * console.log(appSubscriptions)\n * };\n * ```\n * ```ts\n * // shopify.server.ts\n * import { shopifyApp, BillingInterval } from \"@shopify/shopify-app-remix/server\";\n *\n * export const MONTHLY_PLAN = 'Monthly subscription';\n * export const ANNUAL_PLAN = 'Annual subscription';\n *\n * const shopify = shopifyApp({\n * // ...etc\n * billing: {\n * [MONTHLY_PLAN]: {\n * amount: 5,\n * currencyCode: 'USD',\n * interval: BillingInterval.Every30Days,\n * },\n * [ANNUAL_PLAN]: {\n * amount: 50,\n * currencyCode: 'USD',\n * interval: BillingInterval.Annual,\n * },\n * }\n * });\n * export default shopify;\n * export const authenticate = shopify.authenticate;\n * ```\n *\n */\n check: (\n options: CheckBillingOptions,\n ) => Promise;\n\n /**\n * Requests payment for the plan.\n *\n * @returns Redirects to the confirmation URL for the payment.\n *\n * @example\n * Using a custom return URL.\n * Change where the merchant is returned to after approving the purchase using the `returnUrl` option.\n * ```ts\n * // /app/routes/**\\/*.ts\n * import { LoaderFunctionArgs } from \"@remix-run/node\";\n * import { authenticate, MONTHLY_PLAN } from \"../shopify.server\";\n *\n * export const loader = async ({ request }: LoaderFunctionArgs) => {\n * const { billing } = await authenticate.admin(request);\n * await billing.require({\n * plans: [MONTHLY_PLAN],\n * onFailure: async () => billing.request({\n * plan: MONTHLY_PLAN,\n * isTest: true,\n * returnUrl: 'https://admin.shopify.com/store/my-store/apps/my-app/billing-page',\n * }),\n * });\n *\n * // App logic\n * };\n * ```\n * ```ts\n * // shopify.server.ts\n * import { shopifyApp, BillingInterval } from \"@shopify/shopify-app-remix/server\";\n *\n * export const MONTHLY_PLAN = 'Monthly subscription';\n * export const ANNUAL_PLAN = 'Annual subscription';\n *\n * const shopify = shopifyApp({\n * // ...etc\n * billing: {\n * [MONTHLY_PLAN]: {\n * amount: 5,\n * currencyCode: 'USD',\n * interval: BillingInterval.Every30Days,\n * },\n * [ANNUAL_PLAN]: {\n * amount: 50,\n * currencyCode: 'USD',\n * interval: BillingInterval.Annual,\n * },\n * }\n * });\n * export default shopify;\n * export const authenticate = shopify.authenticate;\n * ```\n */\n request: (options: RequestBillingOptions) => Promise;\n\n /**\n * Cancels an ongoing subscription, given its ID.\n *\n * @returns The cancelled subscription.\n *\n * @example\n * Cancelling a subscription.\n * Use the `billing.cancel` function to cancel an active subscription with the id returned from `billing.require`.\n * ```ts\n * // /app/routes/cancel-subscription.ts\n * import { LoaderFunctionArgs } from \"@remix-run/node\";\n * import { authenticate, MONTHLY_PLAN } from \"../shopify.server\";\n *\n * export const loader = async ({ request }: LoaderFunctionArgs) => {\n * const { billing } = await authenticate.admin(request);\n * const billingCheck = await billing.require({\n * plans: [MONTHLY_PLAN],\n * onFailure: async () => billing.request({ plan: MONTHLY_PLAN }),\n * });\n *\n * const subscription = billingCheck.appSubscriptions[0];\n * const cancelledSubscription = await billing.cancel({\n * subscriptionId: subscription.id,\n * isTest: true,\n * prorate: true,\n * });\n *\n * // App logic\n * };\n * ```\n * ```ts\n * // shopify.server.ts\n * import { shopifyApp, BillingInterval } from \"@shopify/shopify-app-remix/server\";\n *\n * export const MONTHLY_PLAN = 'Monthly subscription';\n * export const ANNUAL_PLAN = 'Annual subscription';\n *\n * const shopify = shopifyApp({\n * // ...etc\n * billing: {\n * [MONTHLY_PLAN]: {\n * amount: 5,\n * currencyCode: 'USD',\n * interval: BillingInterval.Every30Days,\n * },\n * [ANNUAL_PLAN]: {\n * amount: 50,\n * currencyCode: 'USD',\n * interval: BillingInterval.Annual,\n * },\n * }\n * });\n * export default shopify;\n * export const authenticate = shopify.authenticate;\n * ```\n */\n cancel: (options: CancelBillingOptions) => Promise;\n}" }, "RequireBillingOptions": { "filePath": "src/server/authenticate/admin/billing/types.ts", @@ -2126,7 +2268,7 @@ }, { "title": "shopify.server.ts", - "code": "import { shopifyApp, BillingInterval } from \"@shopify/shopify-app-remix/server\";\n\nexport const MONTHLY_PLAN = 'Monthly subscription';\nexport const ANNUAL_PLAN = 'Annual subscription';\n\nconst shopify = shopifyApp({\n // ...etc\n billing: {\n [MONTHLY_PLAN]: {\n lineItems: [\n {\n amount: 5,\n currencyCode: 'USD',\n interval: BillingInterval.Every30Days,\n }\n {\n amount: 1,\n currencyCode: 'USD',\n interval: BillingInterval.Usage.\n terms: '1 dollar per 1000 emails',\n },\n ],\n },\n }\n future: {v3_lineItemBilling: true}\n});\nexport default shopify;\nexport const authenticate = shopify.authenticate;", + "code": "import { shopifyApp, BillingInterval } from \"@shopify/shopify-app-remix/server\";\n\nexport const MONTHLY_PLAN = 'Monthly subscription';\nexport const ANNUAL_PLAN = 'Annual subscription';\n\nconst shopify = shopifyApp({\n // ...etc\n billing: {\n [MONTHLY_PLAN]: {\n lineItems: [\n {\n amount: 5,\n currencyCode: 'USD',\n interval: BillingInterval.Every30Days,\n },\n {\n amount: 1,\n currencyCode: 'USD',\n interval: BillingInterval.Usage.\n terms: '1 dollar per 1000 emails',\n },\n ],\n },\n }\n future: {v3_lineItemBilling: true}\n});\nexport default shopify;\nexport const authenticate = shopify.authenticate;", "language": "typescript" } ] @@ -2255,7 +2397,7 @@ "description": "Use the session associated with this request to use REST resources.", "tabs": [ { - "code": "import { ActionFunction } from \"@remix-run/node\";\nimport { authenticate } from \"../shopify.server\";\n\nexport const action: ActionFunction = async ({ request }) => {\n const { session, admin } = await authenticate.flow(request);\n\n const products = await admin?.rest.resources.Product.all({ session });\n // Use products\n\n return new Response();\n};", + "code": "import { ActionFunctionArgs } from \"@remix-run/node\";\nimport { authenticate } from \"../shopify.server\";\n\nexport const action = async ({ request }: ActionFunctionArgs) => {\n const { session, admin } = await authenticate.flow(request);\n\n const products = await admin?.rest.resources.Product.all({ session });\n // Use products\n\n return new Response();\n};", "title": "/app/routes/flow.tsx" } ] @@ -2274,7 +2416,7 @@ "description": "Get the request's POST payload.", "tabs": [ { - "code": "import { ActionFunction } from \"@remix-run/node\";\nimport { authenticate } from \"../shopify.server\";\n\nexport const action: ActionFunction = async ({ request }) => {\n const { payload } = await authenticate.flow(request);\n return new Response();\n};", + "code": "import { ActionFunctionArgs } from \"@remix-run/node\";\nimport { authenticate } from \"../shopify.server\";\n\nexport const action = async ({ request }: ActionFunctionArgs) => {\n const { payload } = await authenticate.flow(request);\n return new Response();\n};", "title": "/app/routes/flow.tsx" } ] @@ -2301,7 +2443,7 @@ ] } ], - "value": "export interface FlowContext<\n Resources extends ShopifyRestResources = ShopifyRestResources,\n> {\n /**\n * A session with an offline token for the shop.\n *\n * Returned only if there is a session for the shop.\n *\n * @example\n * Shopify session for the Flow request.\n * Use the session associated with this request to use REST resources.\n * ```ts\n * // /app/routes/flow.tsx\n * import { ActionFunction } from \"@remix-run/node\";\n * import { authenticate } from \"../shopify.server\";\n *\n * export const action: ActionFunction = async ({ request }) => {\n * const { session, admin } = await authenticate.flow(request);\n *\n * const products = await admin?.rest.resources.Product.all({ session });\n * // Use products\n *\n * return new Response();\n * };\n * ```\n */\n session: Session;\n\n /**\n * The payload from the Flow request.\n *\n * @example\n * Flow payload.\n * Get the request's POST payload.\n * ```ts\n * // /app/routes/flow.tsx\n * import { ActionFunction } from \"@remix-run/node\";\n * import { authenticate } from \"../shopify.server\";\n *\n * export const action: ActionFunction = async ({ request }) => {\n * const { payload } = await authenticate.flow(request);\n * return new Response();\n * };\n * ```\n */\n payload: any;\n\n /**\n * An admin context for the Flow request.\n *\n * Returned only if there is a session for the shop.\n *\n * @example\n * Flow admin context.\n * Use the `admin` object in the context to interact with the Admin API.\n * ```ts\n * // /app/routes/flow.tsx\n * import { ActionFunctionArgs } from \"@remix-run/node\";\n * import { authenticate } from \"../shopify.server\";\n *\n * export async function action({ request }: ActionFunctionArgs) {\n * const { admin } = await authenticate.flow(request);\n *\n * const response = await admin?.graphql(\n * `#graphql\n * mutation populateProduct($input: ProductInput!) {\n * productCreate(input: $input) {\n * product {\n * id\n * }\n * }\n * }`,\n * { variables: { input: { title: \"Product Name\" } } }\n * );\n *\n * const productData = await response.json();\n * return json({ data: productData.data });\n * }\n * ```\n */\n admin: AdminApiContext;\n}" + "value": "export interface FlowContext<\n Resources extends ShopifyRestResources = ShopifyRestResources,\n> {\n /**\n * A session with an offline token for the shop.\n *\n * Returned only if there is a session for the shop.\n *\n * @example\n * Shopify session for the Flow request.\n * Use the session associated with this request to use REST resources.\n * ```ts\n * // /app/routes/flow.tsx\n * import { ActionFunctionArgs } from \"@remix-run/node\";\n * import { authenticate } from \"../shopify.server\";\n *\n * export const action = async ({ request }: ActionFunctionArgs) => {\n * const { session, admin } = await authenticate.flow(request);\n *\n * const products = await admin?.rest.resources.Product.all({ session });\n * // Use products\n *\n * return new Response();\n * };\n * ```\n */\n session: Session;\n\n /**\n * The payload from the Flow request.\n *\n * @example\n * Flow payload.\n * Get the request's POST payload.\n * ```ts\n * // /app/routes/flow.tsx\n * import { ActionFunctionArgs } from \"@remix-run/node\";\n * import { authenticate } from \"../shopify.server\";\n *\n * export const action = async ({ request }: ActionFunctionArgs) => {\n * const { payload } = await authenticate.flow(request);\n * return new Response();\n * };\n * ```\n */\n payload: any;\n\n /**\n * An admin context for the Flow request.\n *\n * Returned only if there is a session for the shop.\n *\n * @example\n * Flow admin context.\n * Use the `admin` object in the context to interact with the Admin API.\n * ```ts\n * // /app/routes/flow.tsx\n * import { ActionFunctionArgs } from \"@remix-run/node\";\n * import { authenticate } from \"../shopify.server\";\n *\n * export async function action({ request }: ActionFunctionArgs) {\n * const { admin } = await authenticate.flow(request);\n *\n * const response = await admin?.graphql(\n * `#graphql\n * mutation populateProduct($input: ProductInput!) {\n * productCreate(input: $input) {\n * product {\n * id\n * }\n * }\n * }`,\n * { variables: { input: { title: \"Product Name\" } } }\n * );\n *\n * const productData = await response.json();\n * return json({ data: productData.data });\n * }\n * ```\n */\n admin: AdminApiContext;\n}" }, "Session": { "filePath": "../../node_modules/@shopify/shopify-api/lib/session/session.d.ts", @@ -2559,7 +2701,7 @@ "description": "Getting the number of orders in a store using REST resources. Visit the [Admin REST API references](/docs/api/admin-rest) for examples on using each resource.", "tabs": [ { - "code": "import { LoaderFunctionArgs, json } from \"@remix-run/node\";\nimport { authenticate } from \"../shopify.server\";\n\nexport const loader = async ({ request }: LoaderFunctionArgs) => {\n const { admin, session } = await authenticate.admin(request);\n return json(admin.rest.resources.Order.count({ session }));\n};", + "code": "import { LoaderFunctionArgs, json } from \"@remix-run/node\";\nimport { authenticate } from \"../shopify.server\";\n\nexport const loader = async ({ request }: LoaderFunctionArgs) => {\n const {\n admin,\n session,\n } = await authenticate.admin(request);\n\n return json(\n admin.rest.resources.Order.count({ session }),\n );\n};", "title": "/app/routes/**\\/*.ts" }, { @@ -2573,7 +2715,7 @@ "description": "Use `admin.rest.get` to make custom requests to make a request to to the `customer/count` endpoint", "tabs": [ { - "code": "import { LoaderFunctionArgs, json } from \"@remix-run/node\";\nimport { authenticate } from \"../shopify.server\";\n\nexport const loader = async ({ request }: LoaderFunctionArgs) => {\n const { admin, session } = await authenticate.admin(request);\n const response = await admin.rest.get({ path: \"/customers/count.json\" });\n const customers = await response.json();\n return json({ customers });\n};", + "code": "import { LoaderFunctionArgs, json } from \"@remix-run/node\";\nimport { authenticate } from \"../shopify.server\";\n\nexport const loader = async ({ request }: LoaderFunctionArgs) => {\n const {\n admin,\n session,\n } = await authenticate.admin(request);\n\n const response = await admin.rest.get({\n path: \"/customers/count.json\",\n });\n const customers = await response.json();\n\n return json({ customers });\n};", "title": "/app/routes/**\\/*.ts" }, { @@ -2587,7 +2729,7 @@ "description": "Use `admin.rest.post` to make custom requests to make a request to to the `customers.json` endpoint to send a welcome email", "tabs": [ { - "code": "import { LoaderFunctionArgs, json } from \"@remix-run/node\";\nimport { authenticate } from \"../shopify.server\";\n\nexport const loader = async ({ request }: LoaderFunctionArgs) => {\n const { admin, session } = await authenticate.admin(request);\n const response = admin.rest.post({\n path: \"customers/7392136888625/send_invite.json\",\n body: {\n customer_invite: {\n to: \"new_test_email@shopify.com\",\n from: \"j.limited@example.com\",\n bcc: [\"j.limited@example.com\"],\n subject: \"Welcome to my new shop\",\n custom_message: \"My awesome new store\",\n },\n },\n});\n const customerInvite = await response.json();\n return json({ customerInvite });\n};", + "code": "import { LoaderFunctionArgs, json } from \"@remix-run/node\";\nimport { authenticate } from \"../shopify.server\";\n\nexport const loader = async ({ request }: LoaderFunctionArgs) => {\n const {\n admin,\n session,\n } = await authenticate.admin(request);\n\n const response = admin.rest.post({\n path: \"customers/7392136888625/send_invite.json\",\n body: {\n customer_invite: {\n to: \"new_test_email@shopify.com\",\n from: \"j.limited@example.com\",\n bcc: [\"j.limited@example.com\"],\n subject: \"Welcome to my new shop\",\n custom_message: \"My awesome new store\",\n },\n },\n });\n\n const customerInvite = await response.json();\n return json({ customerInvite });\n};", "title": "/app/routes/**\\/*.ts" }, { @@ -2610,11 +2752,25 @@ "description": "Use `admin.graphql` to make query / mutation requests.", "tabs": [ { - "code": "import { ActionFunctionArgs } from \"@remix-run/node\";\nimport { authenticate } from \"../shopify.server\";\n\nexport async function action({ request }: ActionFunctionArgs) {\n const { admin } = await authenticate.admin(request);\n\n const response = await admin.graphql(\n `#graphql\n mutation populateProduct($input: ProductInput!) {\n productCreate(input: $input) {\n product {\n id\n }\n }\n }`,\n { variables: { input: { title: \"Product Name\" } } }\n );\n\n const productData = await response.json();\n return json({ data: productData.data });\n}", - "title": "Example" + "code": "import { ActionFunctionArgs } from \"@remix-run/node\";\nimport { authenticate } from \"../shopify.server\";\n\nexport const action = async ({ request }: ActionFunctionArgs) => {\n const { admin } = await authenticate.admin(request);\n\n const response = await admin.graphql(\n `#graphql\n mutation populateProduct($input: ProductInput!) {\n productCreate(input: $input) {\n product {\n id\n }\n }\n }`,\n {\n variables: {\n input: { title: \"Product Name\" },\n },\n },\n );\n\n const productData = await response.json();\n return json({\n productId: productData.data?.productCreate?.product?.id,\n });\n}", + "title": "/app/routes/**\\/*.ts" + }, + { + "code": "import { shopifyApp } from \"@shopify/shopify-app-remix/server\";\n\nconst shopify = shopifyApp({\n // ...\n});\nexport default shopify;\nexport const authenticate = shopify.authenticate;", + "title": "/app/shopify.server.ts" + } + ] + }, + { + "title": "Handling GraphQL errors", + "description": "Catch `GraphqlQueryError` errors to see error messages from the API.", + "tabs": [ + { + "code": "import { ActionFunctionArgs } from \"@remix-run/node\";\nimport { authenticate } from \"../shopify.server\";\n\nexport const action = async ({ request }: ActionFunctionArgs) => {\n const { admin } = await authenticate.admin(request);\n\n try {\n const response = await admin.graphql(\n `#graphql\n query incorrectQuery {\n products(first: 10) {\n nodes {\n not_a_field\n }\n }\n }`,\n );\n\n return json({ data: await response.json() });\n } catch (error) {\n if (error instanceof GraphqlQueryError) {\n // error.body.errors:\n // { graphQLErrors: [\n // { message: \"Field 'not_a_field' doesn't exist on type 'Product'\" }\n // ] }\n return json({ errors: error.body?.errors }, { status: 500 });\n }\n return json({ message: \"An error occurred\" }, { status: 500 });\n }\n}", + "title": "/app/routes/**\\/*.ts" }, { - "code": "import { shopifyApp } from \"@shopify/shopify-app-remix/server\";\n\nconst shopify = shopifyApp({\n restResources,\n // ...etc\n});\nexport default shopify;\nexport const unauthenticated = shopify.unauthenticated;", + "code": "import { shopifyApp } from \"@shopify/shopify-app-remix/server\";\n\nconst shopify = shopifyApp({\n // ...\n});\nexport default shopify;\nexport const authenticate = shopify.authenticate;", "title": "/app/shopify.server.ts" } ] @@ -2622,7 +2778,7 @@ ] } ], - "value": "export interface AdminApiContext<\n Resources extends ShopifyRestResources = ShopifyRestResources,\n> {\n /**\n * Methods for interacting with the Shopify Admin REST API\n *\n * There are methods for interacting with individual REST resources. You can also make `GET`, `POST`, `PUT` and `DELETE` requests should the REST resources not meet your needs.\n *\n * {@link https://shopify.dev/docs/api/admin-rest}\n *\n * @example\n * Using REST resources.\n * Getting the number of orders in a store using REST resources. Visit the [Admin REST API references](/docs/api/admin-rest) for examples on using each resource. \n *\n * ```ts\n * // /app/routes/**\\/*.ts\n * import { LoaderFunctionArgs, json } from \"@remix-run/node\";\n * import { authenticate } from \"../shopify.server\";\n *\n * export const loader = async ({ request }: LoaderFunctionArgs) => {\n * const { admin, session } = await authenticate.admin(request);\n * return json(admin.rest.resources.Order.count({ session }));\n * };\n * ```\n *\n *\n * ```ts\n * // /app/shopify.server.ts\n * import { shopifyApp } from \"@shopify/shopify-app-remix/server\";\n * import { restResources } from \"@shopify/shopify-api/rest/admin/2023-07\";\n *\n * const shopify = shopifyApp({\n * restResources,\n * // ...etc\n * });\n * export default shopify;\n * export const authenticate = shopify.authenticate;\n * ```\n *\n *\n * @example\n * Performing a GET request to the REST API.\n * Use `admin.rest.get` to make custom requests to make a request to to the `customer/count` endpoint\n *\n * ```ts\n * // /app/routes/**\\/*.ts\n * import { LoaderFunctionArgs, json } from \"@remix-run/node\";\n * import { authenticate } from \"../shopify.server\";\n *\n * export const loader = async ({ request }: LoaderFunctionArgs) => {\n * const { admin, session } = await authenticate.admin(request);\n * const response = await admin.rest.get({ path: \"/customers/count.json\" });\n * const customers = await response.json();\n * return json({ customers });\n * };\n * ```\n *\n * ```ts\n * // /app/shopify.server.ts\n * import { shopifyApp } from \"@shopify/shopify-app-remix/server\";\n * import { restResources } from \"@shopify/shopify-api/rest/admin/2023-04\";\n *\n * const shopify = shopifyApp({\n * restResources,\n * // ...etc\n * });\n * export default shopify;\n * export const authenticate = shopify.authenticate;\n * ```\n * @example\n * Performing a POST request to the REST API.\n * Use `admin.rest.post` to make custom requests to make a request to to the `customers.json` endpoint to send a welcome email\n * ```ts\n * // /app/routes/**\\/*.ts\n * import { LoaderFunctionArgs, json } from \"@remix-run/node\";\n * import { authenticate } from \"../shopify.server\";\n *\n * export const loader = async ({ request }: LoaderFunctionArgs) => {\n * const { admin, session } = await authenticate.admin(request);\n * const response = admin.rest.post({\n * path: \"customers/7392136888625/send_invite.json\",\n * body: {\n * customer_invite: {\n * to: \"new_test_email@shopify.com\",\n * from: \"j.limited@example.com\",\n * bcc: [\"j.limited@example.com\"],\n * subject: \"Welcome to my new shop\",\n * custom_message: \"My awesome new store\",\n * },\n * },\n * });\n * const customerInvite = await response.json();\n * return json({ customerInvite });\n * };\n * ```\n *\n * ```ts\n * // /app/shopify.server.ts\n * import { shopifyApp } from \"@shopify/shopify-app-remix/server\";\n * import { restResources } from \"@shopify/shopify-api/rest/admin/2023-04\";\n *\n * const shopify = shopifyApp({\n * restResources,\n * // ...etc\n * });\n * export default shopify;\n * export const authenticate = shopify.authenticate;\n * ```\n */\n rest: RestClientWithResources;\n\n /**\n * Methods for interacting with the Shopify Admin GraphQL API\n *\n * {@link https://shopify.dev/docs/api/admin-graphql}\n * {@link https://github.com/Shopify/shopify-api-js/blob/main/packages/shopify-api/docs/reference/clients/Graphql.md}\n *\n * @example\n * Querying the GraphQL API.\n * Use `admin.graphql` to make query / mutation requests.\n * ```ts\n * import { ActionFunctionArgs } from \"@remix-run/node\";\n * import { authenticate } from \"../shopify.server\";\n *\n * export async function action({ request }: ActionFunctionArgs) {\n * const { admin } = await authenticate.admin(request);\n *\n * const response = await admin.graphql(\n * `#graphql\n * mutation populateProduct($input: ProductInput!) {\n * productCreate(input: $input) {\n * product {\n * id\n * }\n * }\n * }`,\n * { variables: { input: { title: \"Product Name\" } } }\n * );\n *\n * const productData = await response.json();\n * return json({ data: productData.data });\n * }\n * ```\n *\n * ```ts\n * // /app/shopify.server.ts\n * import { shopifyApp } from \"@shopify/shopify-app-remix/server\";\n *\n * const shopify = shopifyApp({\n * restResources,\n * // ...etc\n * });\n * export default shopify;\n * export const unauthenticated = shopify.unauthenticated;\n * ```\n */\n graphql: GraphQLClient;\n}" + "value": "export interface AdminApiContext<\n Resources extends ShopifyRestResources = ShopifyRestResources,\n> {\n /**\n * Methods for interacting with the Shopify Admin REST API\n *\n * There are methods for interacting with individual REST resources. You can also make `GET`, `POST`, `PUT` and `DELETE` requests should the REST resources not meet your needs.\n *\n * {@link https://shopify.dev/docs/api/admin-rest}\n *\n * @example\n * Using REST resources.\n * Getting the number of orders in a store using REST resources. Visit the [Admin REST API references](/docs/api/admin-rest) for examples on using each resource. \n *\n * ```ts\n * // /app/routes/**\\/*.ts\n * import { LoaderFunctionArgs, json } from \"@remix-run/node\";\n * import { authenticate } from \"../shopify.server\";\n *\n * export const loader = async ({ request }: LoaderFunctionArgs) => {\n * const {\n * admin,\n * session,\n * } = await authenticate.admin(request);\n *\n * return json(\n * admin.rest.resources.Order.count({ session }),\n * );\n * };\n * ```\n *\n * ```ts\n * // /app/shopify.server.ts\n * import { shopifyApp } from \"@shopify/shopify-app-remix/server\";\n * import { restResources } from \"@shopify/shopify-api/rest/admin/2023-07\";\n *\n * const shopify = shopifyApp({\n * restResources,\n * // ...etc\n * });\n * export default shopify;\n * export const authenticate = shopify.authenticate;\n * ```\n *\n * @example\n * Performing a GET request to the REST API.\n * Use `admin.rest.get` to make custom requests to make a request to to the `customer/count` endpoint\n *\n * ```ts\n * // /app/routes/**\\/*.ts\n * import { LoaderFunctionArgs, json } from \"@remix-run/node\";\n * import { authenticate } from \"../shopify.server\";\n *\n * export const loader = async ({ request }: LoaderFunctionArgs) => {\n * const {\n * admin,\n * session,\n * } = await authenticate.admin(request);\n *\n * const response = await admin.rest.get({\n * path: \"/customers/count.json\",\n * });\n * const customers = await response.json();\n *\n * return json({ customers });\n * };\n * ```\n *\n * ```ts\n * // /app/shopify.server.ts\n * import { shopifyApp } from \"@shopify/shopify-app-remix/server\";\n * import { restResources } from \"@shopify/shopify-api/rest/admin/2023-04\";\n *\n * const shopify = shopifyApp({\n * restResources,\n * // ...etc\n * });\n * export default shopify;\n * export const authenticate = shopify.authenticate;\n * ```\n *\n * @example\n * Performing a POST request to the REST API.\n * Use `admin.rest.post` to make custom requests to make a request to to the `customers.json` endpoint to send a welcome email\n * ```ts\n * // /app/routes/**\\/*.ts\n * import { LoaderFunctionArgs, json } from \"@remix-run/node\";\n * import { authenticate } from \"../shopify.server\";\n *\n * export const loader = async ({ request }: LoaderFunctionArgs) => {\n * const {\n * admin,\n * session,\n * } = await authenticate.admin(request);\n *\n * const response = admin.rest.post({\n * path: \"customers/7392136888625/send_invite.json\",\n * body: {\n * customer_invite: {\n * to: \"new_test_email@shopify.com\",\n * from: \"j.limited@example.com\",\n * bcc: [\"j.limited@example.com\"],\n * subject: \"Welcome to my new shop\",\n * custom_message: \"My awesome new store\",\n * },\n * },\n * });\n *\n * const customerInvite = await response.json();\n * return json({ customerInvite });\n * };\n * ```\n *\n * ```ts\n * // /app/shopify.server.ts\n * import { shopifyApp } from \"@shopify/shopify-app-remix/server\";\n * import { restResources } from \"@shopify/shopify-api/rest/admin/2023-04\";\n *\n * const shopify = shopifyApp({\n * restResources,\n * // ...etc\n * });\n * export default shopify;\n * export const authenticate = shopify.authenticate;\n * ```\n */\n rest: RestClientWithResources;\n\n /**\n * Methods for interacting with the Shopify Admin GraphQL API\n *\n * {@link https://shopify.dev/docs/api/admin-graphql}\n * {@link https://github.com/Shopify/shopify-api-js/blob/main/packages/shopify-api/docs/reference/clients/Graphql.md}\n *\n * @example\n * Querying the GraphQL API.\n * Use `admin.graphql` to make query / mutation requests.\n * ```ts\n * // /app/routes/**\\/*.ts\n * import { ActionFunctionArgs } from \"@remix-run/node\";\n * import { authenticate } from \"../shopify.server\";\n *\n * export const action = async ({ request }: ActionFunctionArgs) => {\n * const { admin } = await authenticate.admin(request);\n *\n * const response = await admin.graphql(\n * `#graphql\n * mutation populateProduct($input: ProductInput!) {\n * productCreate(input: $input) {\n * product {\n * id\n * }\n * }\n * }`,\n * {\n * variables: {\n * input: { title: \"Product Name\" },\n * },\n * },\n * );\n *\n * const productData = await response.json();\n * return json({\n * productId: productData.data?.productCreate?.product?.id,\n * });\n * }\n * ```\n *\n * ```ts\n * // /app/shopify.server.ts\n * import { shopifyApp } from \"@shopify/shopify-app-remix/server\";\n *\n * const shopify = shopifyApp({\n * // ...\n * });\n * export default shopify;\n * export const authenticate = shopify.authenticate;\n * ```\n *\n * @example\n * Handling GraphQL errors.\n * Catch `GraphqlQueryError` errors to see error messages from the API.\n * ```ts\n * // /app/routes/**\\/*.ts\n * import { ActionFunctionArgs } from \"@remix-run/node\";\n * import { authenticate } from \"../shopify.server\";\n *\n * export const action = async ({ request }: ActionFunctionArgs) => {\n * const { admin } = await authenticate.admin(request);\n *\n * try {\n * const response = await admin.graphql(\n * `#graphql\n * query incorrectQuery {\n * products(first: 10) {\n * nodes {\n * not_a_field\n * }\n * }\n * }`,\n * );\n *\n * return json({ data: await response.json() });\n * } catch (error) {\n * if (error instanceof GraphqlQueryError) {\n * // error.body.errors:\n * // { graphQLErrors: [\n * // { message: \"Field 'not_a_field' doesn't exist on type 'Product'\" }\n * // ] }\n * return json({ errors: error.body?.errors }, { status: 500 });\n * }\n * return json({ message: \"An error occurred\" }, { status: 500 });\n * }\n * }\n * ```\n *\n * ```ts\n * // /app/shopify.server.ts\n * import { shopifyApp } from \"@shopify/shopify-app-remix/server\";\n *\n * const shopify = shopifyApp({\n * // ...\n * });\n * export default shopify;\n * export const authenticate = shopify.authenticate;\n * ```\n */\n graphql: GraphQLClient;\n}" }, "RestClientWithResources": { "filePath": "src/server/clients/admin/rest.ts", @@ -2775,57 +2931,167 @@ "value": "GetRequestParams & {\n data: Record | string;\n}", "description": "" }, - "AdminOperations": { - "filePath": "../../node_modules/@shopify/shopify-api/node_modules/@shopify/admin-api-client/dist/ts/graphql/types.d.ts", - "syntaxKind": "TypeAliasDeclaration", - "name": "AdminOperations", - "value": "AdminQueries & AdminMutations", - "description": "" - }, - "AdminQueries": { - "filePath": "../../node_modules/@shopify/shopify-api/node_modules/@shopify/admin-api-client/dist/ts/graphql/types.d.ts", - "name": "AdminQueries", + "GraphQLClient": { + "filePath": "src/server/clients/types.ts", + "name": "GraphQLClient", "description": "", - "members": [ + "params": [ { - "filePath": "../../node_modules/@shopify/shopify-api/node_modules/@shopify/admin-api-client/dist/ts/graphql/types.d.ts", - "name": "[key: string]", - "value": "{\n variables: any;\n return: any;\n }" + "name": "query", + "description": "", + "value": "Operation extends keyof Operations", + "filePath": "src/server/clients/types.ts" }, { - "filePath": "../../node_modules/@shopify/shopify-api/node_modules/@shopify/admin-api-client/dist/ts/graphql/types.d.ts", - "name": "[key: number | symbol]", - "value": "never" + "name": "options", + "description": "", + "value": "GraphQLQueryOptions", + "isOptional": true, + "filePath": "src/server/clients/types.ts" } ], - "value": "export interface AdminQueries {\n [key: string]: {\n variables: any;\n return: any;\n };\n [key: number | symbol]: never;\n}" + "returns": { + "filePath": "src/server/clients/types.ts", + "description": "", + "name": "interface Promise {\n /**\n * Attaches callbacks for the resolution and/or rejection of the Promise.\n * @param onfulfilled The callback to execute when the Promise is resolved.\n * @param onrejected The callback to execute when the Promise is rejected.\n * @returns A Promise for the completion of which ever callback is executed.\n */\n then(onfulfilled?: ((value: T) => TResult1 | PromiseLike) | undefined | null, onrejected?: ((reason: any) => TResult2 | PromiseLike) | undefined | null): Promise;\n\n /**\n * Attaches a callback for only the rejection of the Promise.\n * @param onrejected The callback to execute when the Promise is rejected.\n * @returns A Promise for the completion of the callback.\n */\n catch(onrejected?: ((reason: any) => TResult | PromiseLike) | undefined | null): Promise;\n}, interface Promise {}, Promise: PromiseConstructor, interface Promise {\n readonly [Symbol.toStringTag]: string;\n}, interface Promise {\n /**\n * Attaches a callback that is invoked when the Promise is settled (fulfilled or rejected). The\n * resolved value cannot be modified from the callback.\n * @param onfinally The callback to execute when the Promise is settled (fulfilled or rejected).\n * @returns A Promise for the completion of the callback.\n */\n finally(onfinally?: (() => void) | undefined | null): Promise;\n}", + "value": "interface Promise {\n /**\n * Attaches callbacks for the resolution and/or rejection of the Promise.\n * @param onfulfilled The callback to execute when the Promise is resolved.\n * @param onrejected The callback to execute when the Promise is rejected.\n * @returns A Promise for the completion of which ever callback is executed.\n */\n then(onfulfilled?: ((value: T) => TResult1 | PromiseLike) | undefined | null, onrejected?: ((reason: any) => TResult2 | PromiseLike) | undefined | null): Promise;\n\n /**\n * Attaches a callback for only the rejection of the Promise.\n * @param onrejected The callback to execute when the Promise is rejected.\n * @returns A Promise for the completion of the callback.\n */\n catch(onrejected?: ((reason: any) => TResult | PromiseLike) | undefined | null): Promise;\n}, interface Promise {}, Promise: PromiseConstructor, interface Promise {\n readonly [Symbol.toStringTag]: string;\n}, interface Promise {\n /**\n * Attaches a callback that is invoked when the Promise is settled (fulfilled or rejected). The\n * resolved value cannot be modified from the callback.\n * @param onfinally The callback to execute when the Promise is settled (fulfilled or rejected).\n * @returns A Promise for the completion of the callback.\n */\n finally(onfinally?: (() => void) | undefined | null): Promise;\n}" + }, + "value": "export type GraphQLClient = <\n Operation extends keyof Operations,\n>(\n query: Operation,\n options?: GraphQLQueryOptions,\n) => Promise>;" }, - "AdminMutations": { - "filePath": "../../node_modules/@shopify/shopify-api/node_modules/@shopify/admin-api-client/dist/ts/graphql/types.d.ts", - "name": "AdminMutations", + "GraphQLQueryOptions": { + "filePath": "src/server/clients/types.ts", + "name": "GraphQLQueryOptions", "description": "", "members": [ { - "filePath": "../../node_modules/@shopify/shopify-api/node_modules/@shopify/admin-api-client/dist/ts/graphql/types.d.ts", - "name": "[key: string]", - "value": "{\n variables: any;\n return: any;\n }" + "filePath": "src/server/clients/types.ts", + "syntaxKind": "PropertySignature", + "name": "variables", + "value": "ApiClientRequestOptions[\"variables\"]", + "description": "The variables to pass to the operation.", + "isOptional": true }, { - "filePath": "../../node_modules/@shopify/shopify-api/node_modules/@shopify/admin-api-client/dist/ts/graphql/types.d.ts", - "name": "[key: number | symbol]", - "value": "never" + "filePath": "src/server/clients/types.ts", + "syntaxKind": "PropertySignature", + "name": "apiVersion", + "value": "ApiVersion", + "description": "The version of the API to use for the request.", + "isOptional": true + }, + { + "filePath": "src/server/clients/types.ts", + "syntaxKind": "PropertySignature", + "name": "headers", + "value": "Record", + "description": "Additional headers to include in the request.", + "isOptional": true + }, + { + "filePath": "src/server/clients/types.ts", + "syntaxKind": "PropertySignature", + "name": "tries", + "value": "number", + "description": "The total number of times to try the request if it fails.", + "isOptional": true } ], - "value": "export interface AdminMutations {\n [key: string]: {\n variables: any;\n return: any;\n };\n [key: number | symbol]: never;\n}" - } - } - } - ], - "jsDocTypeExamples": [ - "FlowContext" - ], - "related": [ - { + "value": "export interface GraphQLQueryOptions<\n Operation extends keyof Operations,\n Operations extends AllOperations,\n> {\n /**\n * The variables to pass to the operation.\n */\n variables?: ApiClientRequestOptions['variables'];\n /**\n * The version of the API to use for the request.\n */\n apiVersion?: ApiVersion;\n /**\n * Additional headers to include in the request.\n */\n headers?: Record;\n /**\n * The total number of times to try the request if it fails.\n */\n tries?: number;\n}" + }, + "ApiVersion": { + "filePath": "../../node_modules/@shopify/shopify-api/lib/types.d.ts", + "syntaxKind": "EnumDeclaration", + "name": "ApiVersion", + "value": "export declare enum ApiVersion {\n October22 = \"2022-10\",\n January23 = \"2023-01\",\n April23 = \"2023-04\",\n July23 = \"2023-07\",\n October23 = \"2023-10\",\n January24 = \"2024-01\",\n Unstable = \"unstable\"\n}", + "members": [ + { + "filePath": "../../node_modules/@shopify/shopify-api/lib/types.d.ts", + "name": "October22", + "value": "2022-10" + }, + { + "filePath": "../../node_modules/@shopify/shopify-api/lib/types.d.ts", + "name": "January23", + "value": "2023-01" + }, + { + "filePath": "../../node_modules/@shopify/shopify-api/lib/types.d.ts", + "name": "April23", + "value": "2023-04" + }, + { + "filePath": "../../node_modules/@shopify/shopify-api/lib/types.d.ts", + "name": "July23", + "value": "2023-07" + }, + { + "filePath": "../../node_modules/@shopify/shopify-api/lib/types.d.ts", + "name": "October23", + "value": "2023-10" + }, + { + "filePath": "../../node_modules/@shopify/shopify-api/lib/types.d.ts", + "name": "January24", + "value": "2024-01" + }, + { + "filePath": "../../node_modules/@shopify/shopify-api/lib/types.d.ts", + "name": "Unstable", + "value": "unstable" + } + ] + }, + "AdminOperations": { + "filePath": "../../node_modules/@shopify/shopify-api/node_modules/@shopify/admin-api-client/dist/ts/graphql/types.d.ts", + "syntaxKind": "TypeAliasDeclaration", + "name": "AdminOperations", + "value": "AdminQueries & AdminMutations", + "description": "" + }, + "AdminQueries": { + "filePath": "../../node_modules/@shopify/shopify-api/node_modules/@shopify/admin-api-client/dist/ts/graphql/types.d.ts", + "name": "AdminQueries", + "description": "", + "members": [ + { + "filePath": "../../node_modules/@shopify/shopify-api/node_modules/@shopify/admin-api-client/dist/ts/graphql/types.d.ts", + "name": "[key: string]", + "value": "{\n variables: any;\n return: any;\n }" + }, + { + "filePath": "../../node_modules/@shopify/shopify-api/node_modules/@shopify/admin-api-client/dist/ts/graphql/types.d.ts", + "name": "[key: number | symbol]", + "value": "never" + } + ], + "value": "export interface AdminQueries {\n [key: string]: {\n variables: any;\n return: any;\n };\n [key: number | symbol]: never;\n}" + }, + "AdminMutations": { + "filePath": "../../node_modules/@shopify/shopify-api/node_modules/@shopify/admin-api-client/dist/ts/graphql/types.d.ts", + "name": "AdminMutations", + "description": "", + "members": [ + { + "filePath": "../../node_modules/@shopify/shopify-api/node_modules/@shopify/admin-api-client/dist/ts/graphql/types.d.ts", + "name": "[key: string]", + "value": "{\n variables: any;\n return: any;\n }" + }, + { + "filePath": "../../node_modules/@shopify/shopify-api/node_modules/@shopify/admin-api-client/dist/ts/graphql/types.d.ts", + "name": "[key: number | symbol]", + "value": "never" + } + ], + "value": "export interface AdminMutations {\n [key: string]: {\n variables: any;\n return: any;\n };\n [key: number | symbol]: never;\n}" + } + } + } + ], + "jsDocTypeExamples": [ + "FlowContext" + ], + "related": [ + { "name": "Admin API context", "subtitle": "Interact with the Admin API.", "url": "/docs/api/shopify-app-remix/apis/admin-api" @@ -2850,7 +3116,7 @@ "tabs": [ { "title": "/app/routes/flow.tsx", - "code": "import { ActionFunction } from \"@remix-run/node\";\nimport { authenticate } from \"../shopify.server\";\n\nexport const action: ActionFunction = async ({ request }) => {\n const { session, admin } = await authenticate.flow(request);\n\n const products = await admin?.rest.resources.Product.all({ session });\n // Use products\n\n return new Response();\n};", + "code": "import { ActionFunctionArgs } from \"@remix-run/node\";\nimport { authenticate } from \"../shopify.server\";\n\nexport const action = async ({ request }: ActionFunctionArgs) => {\n const { session, admin } = await authenticate.flow(request);\n\n const products = await admin?.rest.resources.Product.all({ session });\n // Use products\n\n return new Response();\n};", "language": "typescript" } ] @@ -2868,7 +3134,7 @@ "tabs": [ { "title": "/app/routes/flow.tsx", - "code": "import { ActionFunction } from \"@remix-run/node\";\nimport { authenticate } from \"../shopify.server\";\n\nexport const action: ActionFunction = async ({ request }) => {\n const { payload } = await authenticate.flow(request);\n return new Response();\n};", + "code": "import { ActionFunctionArgs } from \"@remix-run/node\";\nimport { authenticate } from \"../shopify.server\";\n\nexport const action = async ({ request }: ActionFunctionArgs) => {\n const { payload } = await authenticate.flow(request);\n return new Response();\n};", "language": "typescript" } ] @@ -2965,7 +3231,7 @@ "examples": [ { "title": "Rendering liquid content", - "description": "Use the `liquid` helper to render a `Response` with Liquid content using the shop's theme.", + "description": "Use the `liquid` helper to render a `Response` with Liquid content using the shop's theme. See the [Liquid reference](https://shopify.dev/docs/api/liquid) for all the features it enables.", "tabs": [ { "code": "import {authenticate} from \"~/shopify.server\"\n\nexport async function loader({ request }) {\n const {liquid} = await authenticate.public.appProxy(request);\n\n return liquid(\"Hello {{shop.name}}\");\n}", @@ -3048,7 +3314,7 @@ "description": "Get the session for the shop that initiated the request to the app proxy.", "tabs": [ { - "code": "import { json } from \"@remix-run/node\";\nimport { authenticate } from \"../shopify.server\";\nimport { getMyAppModelData } from \"~/db/model.server\";\n\nexport const loader = async ({ request }) => {\n // Get the session for the shop that initiated the request to the app proxy.\n const { session } = await authenticate.public.appProxy(request);\n\n // Use the session data to make to queries to your database or additional requests.\n return json(await getMyAppModelData({shop: session.shop));\n};", + "code": "import { json } from \"@remix-run/node\";\nimport { authenticate } from \"../shopify.server\";\nimport { getMyAppModelData } from \"~/db/model.server\";\n\nexport const loader = async ({ request }) => {\n // Get the session for the shop that initiated the request to the app proxy.\n const { session } =\n await authenticate.public.appProxy(request);\n\n // Use the session data to make to queries to your database or additional requests.\n return json(\n await getMyAppModelData({shop: session.shop})\n );\n};", "title": "app/routes/**\\/.ts" } ] @@ -3067,7 +3333,7 @@ "description": "Use the `admin` object to interact with the REST or GraphQL APIs.", "tabs": [ { - "code": "import { json } from \"@remix-run/node\";\nimport { authenticate } from \"../shopify.server\";\n\nexport async function action({ request }: ActionFunctionArgs) {\n const { admin } = await authenticate.public.appProxy(request);\n\n const response = await admin.graphql(\n `#graphql\n mutation populateProduct($input: ProductInput!) {\n productCreate(input: $input) {\n product {\n id\n }\n }\n }`,\n { variables: { input: { title: \"Product Name\" } } }\n );\n\n const productData = await response.json();\n return json({ data: productData.data });\n}", + "code": "import { json } from \"@remix-run/node\";\nimport { authenticate } from \"../shopify.server\";\n\nexport async function action({ request }: ActionFunctionArgs) {\n const { admin } = await authenticate.public.appProxy(request);\n\n const response = await admin.graphql(\n `#graphql\n mutation populateProduct($input: ProductInput!) {\n productCreate(input: $input) {\n product {\n id\n }\n }\n }`,\n {\n variables: {\n input: { title: \"Product Name\" }\n }\n }\n );\n\n const productData = await response.json();\n return json({ data: productData.data });\n}", "title": "app/routes/**\\/.ts" } ] @@ -3086,7 +3352,7 @@ "description": "Use the `storefront` object to interact with the GraphQL API.", "tabs": [ { - "code": "import { json } from \"@remix-run/node\";\nimport { authenticate } from \"../shopify.server\";\n\nexport async function action({ request }: ActionFunctionArgs) {\n const { storefront } = await authenticate.public.appProxy(request);\n\n const response = await storefront.graphql(`{blogs(first: 10) { edges { node { id } } } }`);\n\n return json(await response.json());\n}", + "code": "import { json } from \"@remix-run/node\";\nimport { authenticate } from \"../shopify.server\";\n\nexport async function action({ request }: ActionFunctionArgs) {\n const { storefront } = await authenticate.public.appProxy(request);\n\n const response = await storefront.graphql(\n `#graphql\n query blogIds {\n blogs(first: 10) {\n edges {\n node {\n id\n }\n }\n }\n }`\n );\n\n return json(await response.json());\n}", "title": "app/routes/**\\/.ts" } ] @@ -3102,7 +3368,7 @@ "examples": [ { "title": "Rendering liquid content", - "description": "Use the `liquid` helper to render a `Response` with Liquid content using the shop's theme.", + "description": "Use the `liquid` helper to render a `Response` with Liquid content using the shop's theme. See the [Liquid reference](https://shopify.dev/docs/api/liquid) for all the features it enables.", "tabs": [ { "code": "import {authenticate} from \"~/shopify.server\"\n\nexport async function loader({ request }) {\n const {liquid} = await authenticate.public.appProxy(request);\n\n return liquid(\"Hello {{shop.name}}\");\n}", @@ -3123,7 +3389,7 @@ ] } ], - "value": "export interface AppProxyContextWithSession<\n Resources extends ShopifyRestResources = ShopifyRestResources,\n> extends Context {\n /**\n * The session for the shop that made the request.\n *\n * This comes from the session storage which `shopifyApp` uses to store sessions in your database of choice.\n *\n * Use this to get shop or user-specific data.\n *\n * @example\n * Using the session object.\n * Get the session for the shop that initiated the request to the app proxy.\n * ```ts\n * // app/routes/**\\/.ts\n * import { json } from \"@remix-run/node\";\n * import { authenticate } from \"../shopify.server\";\n * import { getMyAppModelData } from \"~/db/model.server\";\n *\n * export const loader = async ({ request }) => {\n * // Get the session for the shop that initiated the request to the app proxy.\n * const { session } = await authenticate.public.appProxy(request);\n *\n * // Use the session data to make to queries to your database or additional requests.\n * return json(await getMyAppModelData({shop: session.shop));\n * };\n * ```\n */\n session: Session;\n\n /**\n * Methods for interacting with the GraphQL / REST Admin APIs for the store that made the request.\n *\n * @example\n * Interacting with the Admin API.\n * Use the `admin` object to interact with the REST or GraphQL APIs.\n * ```ts\n * // app/routes/**\\/.ts\n * import { json } from \"@remix-run/node\";\n * import { authenticate } from \"../shopify.server\";\n *\n * export async function action({ request }: ActionFunctionArgs) {\n * const { admin } = await authenticate.public.appProxy(request);\n *\n * const response = await admin.graphql(\n * `#graphql\n * mutation populateProduct($input: ProductInput!) {\n * productCreate(input: $input) {\n * product {\n * id\n * }\n * }\n * }`,\n * { variables: { input: { title: \"Product Name\" } } }\n * );\n *\n * const productData = await response.json();\n * return json({ data: productData.data });\n * }\n * ```\n */\n admin: AdminApiContext;\n\n /**\n * Method for interacting with the Shopify Storefront Graphql API for the store that made the request.\n *\n * @example\n * Interacting with the Storefront API.\n * Use the `storefront` object to interact with the GraphQL API.\n * ```ts\n * // app/routes/**\\/.ts\n * import { json } from \"@remix-run/node\";\n * import { authenticate } from \"../shopify.server\";\n *\n * export async function action({ request }: ActionFunctionArgs) {\n * const { storefront } = await authenticate.public.appProxy(request);\n *\n * const response = await storefront.graphql(`{blogs(first: 10) { edges { node { id } } } }`);\n *\n * return json(await response.json());\n * }\n * ```\n */\n storefront: StorefrontContext;\n}" + "value": "export interface AppProxyContextWithSession<\n Resources extends ShopifyRestResources = ShopifyRestResources,\n> extends Context {\n /**\n * The session for the shop that made the request.\n *\n * This comes from the session storage which `shopifyApp` uses to store sessions in your database of choice.\n *\n * Use this to get shop or user-specific data.\n *\n * @example\n * Using the session object.\n * Get the session for the shop that initiated the request to the app proxy.\n * ```ts\n * // app/routes/**\\/.ts\n * import { json } from \"@remix-run/node\";\n * import { authenticate } from \"../shopify.server\";\n * import { getMyAppModelData } from \"~/db/model.server\";\n *\n * export const loader = async ({ request }) => {\n * // Get the session for the shop that initiated the request to the app proxy.\n * const { session } =\n * await authenticate.public.appProxy(request);\n *\n * // Use the session data to make to queries to your database or additional requests.\n * return json(\n * await getMyAppModelData({shop: session.shop})\n * );\n * };\n * ```\n */\n session: Session;\n\n /**\n * Methods for interacting with the GraphQL / REST Admin APIs for the store that made the request.\n *\n * @example\n * Interacting with the Admin API.\n * Use the `admin` object to interact with the REST or GraphQL APIs.\n * ```ts\n * // app/routes/**\\/.ts\n * import { json } from \"@remix-run/node\";\n * import { authenticate } from \"../shopify.server\";\n *\n * export async function action({ request }: ActionFunctionArgs) {\n * const { admin } = await authenticate.public.appProxy(request);\n *\n * const response = await admin.graphql(\n * `#graphql\n * mutation populateProduct($input: ProductInput!) {\n * productCreate(input: $input) {\n * product {\n * id\n * }\n * }\n * }`,\n * {\n * variables: {\n * input: { title: \"Product Name\" }\n * }\n * }\n * );\n *\n * const productData = await response.json();\n * return json({ data: productData.data });\n * }\n * ```\n */\n admin: AdminApiContext;\n\n /**\n * Method for interacting with the Shopify Storefront Graphql API for the store that made the request.\n *\n * @example\n * Interacting with the Storefront API.\n * Use the `storefront` object to interact with the GraphQL API.\n * ```ts\n * // app/routes/**\\/.ts\n * import { json } from \"@remix-run/node\";\n * import { authenticate } from \"../shopify.server\";\n *\n * export async function action({ request }: ActionFunctionArgs) {\n * const { storefront } = await authenticate.public.appProxy(request);\n *\n * const response = await storefront.graphql(\n * `#graphql\n * query blogIds {\n * blogs(first: 10) {\n * edges {\n * node {\n * id\n * }\n * }\n * }\n * }`\n * );\n *\n * return json(await response.json());\n * }\n * ```\n */\n storefront: StorefrontContext;\n}" }, "Session": { "filePath": "../../node_modules/@shopify/shopify-api/lib/session/session.d.ts", @@ -3381,7 +3647,7 @@ "description": "Getting the number of orders in a store using REST resources. Visit the [Admin REST API references](/docs/api/admin-rest) for examples on using each resource.", "tabs": [ { - "code": "import { LoaderFunctionArgs, json } from \"@remix-run/node\";\nimport { authenticate } from \"../shopify.server\";\n\nexport const loader = async ({ request }: LoaderFunctionArgs) => {\n const { admin, session } = await authenticate.admin(request);\n return json(admin.rest.resources.Order.count({ session }));\n};", + "code": "import { LoaderFunctionArgs, json } from \"@remix-run/node\";\nimport { authenticate } from \"../shopify.server\";\n\nexport const loader = async ({ request }: LoaderFunctionArgs) => {\n const {\n admin,\n session,\n } = await authenticate.admin(request);\n\n return json(\n admin.rest.resources.Order.count({ session }),\n );\n};", "title": "/app/routes/**\\/*.ts" }, { @@ -3395,7 +3661,7 @@ "description": "Use `admin.rest.get` to make custom requests to make a request to to the `customer/count` endpoint", "tabs": [ { - "code": "import { LoaderFunctionArgs, json } from \"@remix-run/node\";\nimport { authenticate } from \"../shopify.server\";\n\nexport const loader = async ({ request }: LoaderFunctionArgs) => {\n const { admin, session } = await authenticate.admin(request);\n const response = await admin.rest.get({ path: \"/customers/count.json\" });\n const customers = await response.json();\n return json({ customers });\n};", + "code": "import { LoaderFunctionArgs, json } from \"@remix-run/node\";\nimport { authenticate } from \"../shopify.server\";\n\nexport const loader = async ({ request }: LoaderFunctionArgs) => {\n const {\n admin,\n session,\n } = await authenticate.admin(request);\n\n const response = await admin.rest.get({\n path: \"/customers/count.json\",\n });\n const customers = await response.json();\n\n return json({ customers });\n};", "title": "/app/routes/**\\/*.ts" }, { @@ -3409,7 +3675,7 @@ "description": "Use `admin.rest.post` to make custom requests to make a request to to the `customers.json` endpoint to send a welcome email", "tabs": [ { - "code": "import { LoaderFunctionArgs, json } from \"@remix-run/node\";\nimport { authenticate } from \"../shopify.server\";\n\nexport const loader = async ({ request }: LoaderFunctionArgs) => {\n const { admin, session } = await authenticate.admin(request);\n const response = admin.rest.post({\n path: \"customers/7392136888625/send_invite.json\",\n body: {\n customer_invite: {\n to: \"new_test_email@shopify.com\",\n from: \"j.limited@example.com\",\n bcc: [\"j.limited@example.com\"],\n subject: \"Welcome to my new shop\",\n custom_message: \"My awesome new store\",\n },\n },\n});\n const customerInvite = await response.json();\n return json({ customerInvite });\n};", + "code": "import { LoaderFunctionArgs, json } from \"@remix-run/node\";\nimport { authenticate } from \"../shopify.server\";\n\nexport const loader = async ({ request }: LoaderFunctionArgs) => {\n const {\n admin,\n session,\n } = await authenticate.admin(request);\n\n const response = admin.rest.post({\n path: \"customers/7392136888625/send_invite.json\",\n body: {\n customer_invite: {\n to: \"new_test_email@shopify.com\",\n from: \"j.limited@example.com\",\n bcc: [\"j.limited@example.com\"],\n subject: \"Welcome to my new shop\",\n custom_message: \"My awesome new store\",\n },\n },\n });\n\n const customerInvite = await response.json();\n return json({ customerInvite });\n};", "title": "/app/routes/**\\/*.ts" }, { @@ -3432,11 +3698,25 @@ "description": "Use `admin.graphql` to make query / mutation requests.", "tabs": [ { - "code": "import { ActionFunctionArgs } from \"@remix-run/node\";\nimport { authenticate } from \"../shopify.server\";\n\nexport async function action({ request }: ActionFunctionArgs) {\n const { admin } = await authenticate.admin(request);\n\n const response = await admin.graphql(\n `#graphql\n mutation populateProduct($input: ProductInput!) {\n productCreate(input: $input) {\n product {\n id\n }\n }\n }`,\n { variables: { input: { title: \"Product Name\" } } }\n );\n\n const productData = await response.json();\n return json({ data: productData.data });\n}", - "title": "Example" + "code": "import { ActionFunctionArgs } from \"@remix-run/node\";\nimport { authenticate } from \"../shopify.server\";\n\nexport const action = async ({ request }: ActionFunctionArgs) => {\n const { admin } = await authenticate.admin(request);\n\n const response = await admin.graphql(\n `#graphql\n mutation populateProduct($input: ProductInput!) {\n productCreate(input: $input) {\n product {\n id\n }\n }\n }`,\n {\n variables: {\n input: { title: \"Product Name\" },\n },\n },\n );\n\n const productData = await response.json();\n return json({\n productId: productData.data?.productCreate?.product?.id,\n });\n}", + "title": "/app/routes/**\\/*.ts" + }, + { + "code": "import { shopifyApp } from \"@shopify/shopify-app-remix/server\";\n\nconst shopify = shopifyApp({\n // ...\n});\nexport default shopify;\nexport const authenticate = shopify.authenticate;", + "title": "/app/shopify.server.ts" + } + ] + }, + { + "title": "Handling GraphQL errors", + "description": "Catch `GraphqlQueryError` errors to see error messages from the API.", + "tabs": [ + { + "code": "import { ActionFunctionArgs } from \"@remix-run/node\";\nimport { authenticate } from \"../shopify.server\";\n\nexport const action = async ({ request }: ActionFunctionArgs) => {\n const { admin } = await authenticate.admin(request);\n\n try {\n const response = await admin.graphql(\n `#graphql\n query incorrectQuery {\n products(first: 10) {\n nodes {\n not_a_field\n }\n }\n }`,\n );\n\n return json({ data: await response.json() });\n } catch (error) {\n if (error instanceof GraphqlQueryError) {\n // error.body.errors:\n // { graphQLErrors: [\n // { message: \"Field 'not_a_field' doesn't exist on type 'Product'\" }\n // ] }\n return json({ errors: error.body?.errors }, { status: 500 });\n }\n return json({ message: \"An error occurred\" }, { status: 500 });\n }\n}", + "title": "/app/routes/**\\/*.ts" }, { - "code": "import { shopifyApp } from \"@shopify/shopify-app-remix/server\";\n\nconst shopify = shopifyApp({\n restResources,\n // ...etc\n});\nexport default shopify;\nexport const unauthenticated = shopify.unauthenticated;", + "code": "import { shopifyApp } from \"@shopify/shopify-app-remix/server\";\n\nconst shopify = shopifyApp({\n // ...\n});\nexport default shopify;\nexport const authenticate = shopify.authenticate;", "title": "/app/shopify.server.ts" } ] @@ -3444,7 +3724,7 @@ ] } ], - "value": "export interface AdminApiContext<\n Resources extends ShopifyRestResources = ShopifyRestResources,\n> {\n /**\n * Methods for interacting with the Shopify Admin REST API\n *\n * There are methods for interacting with individual REST resources. You can also make `GET`, `POST`, `PUT` and `DELETE` requests should the REST resources not meet your needs.\n *\n * {@link https://shopify.dev/docs/api/admin-rest}\n *\n * @example\n * Using REST resources.\n * Getting the number of orders in a store using REST resources. Visit the [Admin REST API references](/docs/api/admin-rest) for examples on using each resource. \n *\n * ```ts\n * // /app/routes/**\\/*.ts\n * import { LoaderFunctionArgs, json } from \"@remix-run/node\";\n * import { authenticate } from \"../shopify.server\";\n *\n * export const loader = async ({ request }: LoaderFunctionArgs) => {\n * const { admin, session } = await authenticate.admin(request);\n * return json(admin.rest.resources.Order.count({ session }));\n * };\n * ```\n *\n *\n * ```ts\n * // /app/shopify.server.ts\n * import { shopifyApp } from \"@shopify/shopify-app-remix/server\";\n * import { restResources } from \"@shopify/shopify-api/rest/admin/2023-07\";\n *\n * const shopify = shopifyApp({\n * restResources,\n * // ...etc\n * });\n * export default shopify;\n * export const authenticate = shopify.authenticate;\n * ```\n *\n *\n * @example\n * Performing a GET request to the REST API.\n * Use `admin.rest.get` to make custom requests to make a request to to the `customer/count` endpoint\n *\n * ```ts\n * // /app/routes/**\\/*.ts\n * import { LoaderFunctionArgs, json } from \"@remix-run/node\";\n * import { authenticate } from \"../shopify.server\";\n *\n * export const loader = async ({ request }: LoaderFunctionArgs) => {\n * const { admin, session } = await authenticate.admin(request);\n * const response = await admin.rest.get({ path: \"/customers/count.json\" });\n * const customers = await response.json();\n * return json({ customers });\n * };\n * ```\n *\n * ```ts\n * // /app/shopify.server.ts\n * import { shopifyApp } from \"@shopify/shopify-app-remix/server\";\n * import { restResources } from \"@shopify/shopify-api/rest/admin/2023-04\";\n *\n * const shopify = shopifyApp({\n * restResources,\n * // ...etc\n * });\n * export default shopify;\n * export const authenticate = shopify.authenticate;\n * ```\n * @example\n * Performing a POST request to the REST API.\n * Use `admin.rest.post` to make custom requests to make a request to to the `customers.json` endpoint to send a welcome email\n * ```ts\n * // /app/routes/**\\/*.ts\n * import { LoaderFunctionArgs, json } from \"@remix-run/node\";\n * import { authenticate } from \"../shopify.server\";\n *\n * export const loader = async ({ request }: LoaderFunctionArgs) => {\n * const { admin, session } = await authenticate.admin(request);\n * const response = admin.rest.post({\n * path: \"customers/7392136888625/send_invite.json\",\n * body: {\n * customer_invite: {\n * to: \"new_test_email@shopify.com\",\n * from: \"j.limited@example.com\",\n * bcc: [\"j.limited@example.com\"],\n * subject: \"Welcome to my new shop\",\n * custom_message: \"My awesome new store\",\n * },\n * },\n * });\n * const customerInvite = await response.json();\n * return json({ customerInvite });\n * };\n * ```\n *\n * ```ts\n * // /app/shopify.server.ts\n * import { shopifyApp } from \"@shopify/shopify-app-remix/server\";\n * import { restResources } from \"@shopify/shopify-api/rest/admin/2023-04\";\n *\n * const shopify = shopifyApp({\n * restResources,\n * // ...etc\n * });\n * export default shopify;\n * export const authenticate = shopify.authenticate;\n * ```\n */\n rest: RestClientWithResources;\n\n /**\n * Methods for interacting with the Shopify Admin GraphQL API\n *\n * {@link https://shopify.dev/docs/api/admin-graphql}\n * {@link https://github.com/Shopify/shopify-api-js/blob/main/packages/shopify-api/docs/reference/clients/Graphql.md}\n *\n * @example\n * Querying the GraphQL API.\n * Use `admin.graphql` to make query / mutation requests.\n * ```ts\n * import { ActionFunctionArgs } from \"@remix-run/node\";\n * import { authenticate } from \"../shopify.server\";\n *\n * export async function action({ request }: ActionFunctionArgs) {\n * const { admin } = await authenticate.admin(request);\n *\n * const response = await admin.graphql(\n * `#graphql\n * mutation populateProduct($input: ProductInput!) {\n * productCreate(input: $input) {\n * product {\n * id\n * }\n * }\n * }`,\n * { variables: { input: { title: \"Product Name\" } } }\n * );\n *\n * const productData = await response.json();\n * return json({ data: productData.data });\n * }\n * ```\n *\n * ```ts\n * // /app/shopify.server.ts\n * import { shopifyApp } from \"@shopify/shopify-app-remix/server\";\n *\n * const shopify = shopifyApp({\n * restResources,\n * // ...etc\n * });\n * export default shopify;\n * export const unauthenticated = shopify.unauthenticated;\n * ```\n */\n graphql: GraphQLClient;\n}" + "value": "export interface AdminApiContext<\n Resources extends ShopifyRestResources = ShopifyRestResources,\n> {\n /**\n * Methods for interacting with the Shopify Admin REST API\n *\n * There are methods for interacting with individual REST resources. You can also make `GET`, `POST`, `PUT` and `DELETE` requests should the REST resources not meet your needs.\n *\n * {@link https://shopify.dev/docs/api/admin-rest}\n *\n * @example\n * Using REST resources.\n * Getting the number of orders in a store using REST resources. Visit the [Admin REST API references](/docs/api/admin-rest) for examples on using each resource. \n *\n * ```ts\n * // /app/routes/**\\/*.ts\n * import { LoaderFunctionArgs, json } from \"@remix-run/node\";\n * import { authenticate } from \"../shopify.server\";\n *\n * export const loader = async ({ request }: LoaderFunctionArgs) => {\n * const {\n * admin,\n * session,\n * } = await authenticate.admin(request);\n *\n * return json(\n * admin.rest.resources.Order.count({ session }),\n * );\n * };\n * ```\n *\n * ```ts\n * // /app/shopify.server.ts\n * import { shopifyApp } from \"@shopify/shopify-app-remix/server\";\n * import { restResources } from \"@shopify/shopify-api/rest/admin/2023-07\";\n *\n * const shopify = shopifyApp({\n * restResources,\n * // ...etc\n * });\n * export default shopify;\n * export const authenticate = shopify.authenticate;\n * ```\n *\n * @example\n * Performing a GET request to the REST API.\n * Use `admin.rest.get` to make custom requests to make a request to to the `customer/count` endpoint\n *\n * ```ts\n * // /app/routes/**\\/*.ts\n * import { LoaderFunctionArgs, json } from \"@remix-run/node\";\n * import { authenticate } from \"../shopify.server\";\n *\n * export const loader = async ({ request }: LoaderFunctionArgs) => {\n * const {\n * admin,\n * session,\n * } = await authenticate.admin(request);\n *\n * const response = await admin.rest.get({\n * path: \"/customers/count.json\",\n * });\n * const customers = await response.json();\n *\n * return json({ customers });\n * };\n * ```\n *\n * ```ts\n * // /app/shopify.server.ts\n * import { shopifyApp } from \"@shopify/shopify-app-remix/server\";\n * import { restResources } from \"@shopify/shopify-api/rest/admin/2023-04\";\n *\n * const shopify = shopifyApp({\n * restResources,\n * // ...etc\n * });\n * export default shopify;\n * export const authenticate = shopify.authenticate;\n * ```\n *\n * @example\n * Performing a POST request to the REST API.\n * Use `admin.rest.post` to make custom requests to make a request to to the `customers.json` endpoint to send a welcome email\n * ```ts\n * // /app/routes/**\\/*.ts\n * import { LoaderFunctionArgs, json } from \"@remix-run/node\";\n * import { authenticate } from \"../shopify.server\";\n *\n * export const loader = async ({ request }: LoaderFunctionArgs) => {\n * const {\n * admin,\n * session,\n * } = await authenticate.admin(request);\n *\n * const response = admin.rest.post({\n * path: \"customers/7392136888625/send_invite.json\",\n * body: {\n * customer_invite: {\n * to: \"new_test_email@shopify.com\",\n * from: \"j.limited@example.com\",\n * bcc: [\"j.limited@example.com\"],\n * subject: \"Welcome to my new shop\",\n * custom_message: \"My awesome new store\",\n * },\n * },\n * });\n *\n * const customerInvite = await response.json();\n * return json({ customerInvite });\n * };\n * ```\n *\n * ```ts\n * // /app/shopify.server.ts\n * import { shopifyApp } from \"@shopify/shopify-app-remix/server\";\n * import { restResources } from \"@shopify/shopify-api/rest/admin/2023-04\";\n *\n * const shopify = shopifyApp({\n * restResources,\n * // ...etc\n * });\n * export default shopify;\n * export const authenticate = shopify.authenticate;\n * ```\n */\n rest: RestClientWithResources;\n\n /**\n * Methods for interacting with the Shopify Admin GraphQL API\n *\n * {@link https://shopify.dev/docs/api/admin-graphql}\n * {@link https://github.com/Shopify/shopify-api-js/blob/main/packages/shopify-api/docs/reference/clients/Graphql.md}\n *\n * @example\n * Querying the GraphQL API.\n * Use `admin.graphql` to make query / mutation requests.\n * ```ts\n * // /app/routes/**\\/*.ts\n * import { ActionFunctionArgs } from \"@remix-run/node\";\n * import { authenticate } from \"../shopify.server\";\n *\n * export const action = async ({ request }: ActionFunctionArgs) => {\n * const { admin } = await authenticate.admin(request);\n *\n * const response = await admin.graphql(\n * `#graphql\n * mutation populateProduct($input: ProductInput!) {\n * productCreate(input: $input) {\n * product {\n * id\n * }\n * }\n * }`,\n * {\n * variables: {\n * input: { title: \"Product Name\" },\n * },\n * },\n * );\n *\n * const productData = await response.json();\n * return json({\n * productId: productData.data?.productCreate?.product?.id,\n * });\n * }\n * ```\n *\n * ```ts\n * // /app/shopify.server.ts\n * import { shopifyApp } from \"@shopify/shopify-app-remix/server\";\n *\n * const shopify = shopifyApp({\n * // ...\n * });\n * export default shopify;\n * export const authenticate = shopify.authenticate;\n * ```\n *\n * @example\n * Handling GraphQL errors.\n * Catch `GraphqlQueryError` errors to see error messages from the API.\n * ```ts\n * // /app/routes/**\\/*.ts\n * import { ActionFunctionArgs } from \"@remix-run/node\";\n * import { authenticate } from \"../shopify.server\";\n *\n * export const action = async ({ request }: ActionFunctionArgs) => {\n * const { admin } = await authenticate.admin(request);\n *\n * try {\n * const response = await admin.graphql(\n * `#graphql\n * query incorrectQuery {\n * products(first: 10) {\n * nodes {\n * not_a_field\n * }\n * }\n * }`,\n * );\n *\n * return json({ data: await response.json() });\n * } catch (error) {\n * if (error instanceof GraphqlQueryError) {\n * // error.body.errors:\n * // { graphQLErrors: [\n * // { message: \"Field 'not_a_field' doesn't exist on type 'Product'\" }\n * // ] }\n * return json({ errors: error.body?.errors }, { status: 500 });\n * }\n * return json({ message: \"An error occurred\" }, { status: 500 });\n * }\n * }\n * ```\n *\n * ```ts\n * // /app/shopify.server.ts\n * import { shopifyApp } from \"@shopify/shopify-app-remix/server\";\n *\n * const shopify = shopifyApp({\n * // ...\n * });\n * export default shopify;\n * export const authenticate = shopify.authenticate;\n * ```\n */\n graphql: GraphQLClient;\n}" }, "RestClientWithResources": { "filePath": "src/server/clients/admin/rest.ts", @@ -3597,6 +3877,116 @@ "value": "GetRequestParams & {\n data: Record | string;\n}", "description": "" }, + "GraphQLClient": { + "filePath": "src/server/clients/types.ts", + "name": "GraphQLClient", + "description": "", + "params": [ + { + "name": "query", + "description": "", + "value": "Operation extends keyof Operations", + "filePath": "src/server/clients/types.ts" + }, + { + "name": "options", + "description": "", + "value": "GraphQLQueryOptions", + "isOptional": true, + "filePath": "src/server/clients/types.ts" + } + ], + "returns": { + "filePath": "src/server/clients/types.ts", + "description": "", + "name": "interface Promise {\n /**\n * Attaches callbacks for the resolution and/or rejection of the Promise.\n * @param onfulfilled The callback to execute when the Promise is resolved.\n * @param onrejected The callback to execute when the Promise is rejected.\n * @returns A Promise for the completion of which ever callback is executed.\n */\n then(onfulfilled?: ((value: T) => TResult1 | PromiseLike) | undefined | null, onrejected?: ((reason: any) => TResult2 | PromiseLike) | undefined | null): Promise;\n\n /**\n * Attaches a callback for only the rejection of the Promise.\n * @param onrejected The callback to execute when the Promise is rejected.\n * @returns A Promise for the completion of the callback.\n */\n catch(onrejected?: ((reason: any) => TResult | PromiseLike) | undefined | null): Promise;\n}, interface Promise {}, Promise: PromiseConstructor, interface Promise {\n readonly [Symbol.toStringTag]: string;\n}, interface Promise {\n /**\n * Attaches a callback that is invoked when the Promise is settled (fulfilled or rejected). The\n * resolved value cannot be modified from the callback.\n * @param onfinally The callback to execute when the Promise is settled (fulfilled or rejected).\n * @returns A Promise for the completion of the callback.\n */\n finally(onfinally?: (() => void) | undefined | null): Promise;\n}", + "value": "interface Promise {\n /**\n * Attaches callbacks for the resolution and/or rejection of the Promise.\n * @param onfulfilled The callback to execute when the Promise is resolved.\n * @param onrejected The callback to execute when the Promise is rejected.\n * @returns A Promise for the completion of which ever callback is executed.\n */\n then(onfulfilled?: ((value: T) => TResult1 | PromiseLike) | undefined | null, onrejected?: ((reason: any) => TResult2 | PromiseLike) | undefined | null): Promise;\n\n /**\n * Attaches a callback for only the rejection of the Promise.\n * @param onrejected The callback to execute when the Promise is rejected.\n * @returns A Promise for the completion of the callback.\n */\n catch(onrejected?: ((reason: any) => TResult | PromiseLike) | undefined | null): Promise;\n}, interface Promise {}, Promise: PromiseConstructor, interface Promise {\n readonly [Symbol.toStringTag]: string;\n}, interface Promise {\n /**\n * Attaches a callback that is invoked when the Promise is settled (fulfilled or rejected). The\n * resolved value cannot be modified from the callback.\n * @param onfinally The callback to execute when the Promise is settled (fulfilled or rejected).\n * @returns A Promise for the completion of the callback.\n */\n finally(onfinally?: (() => void) | undefined | null): Promise;\n}" + }, + "value": "export type GraphQLClient = <\n Operation extends keyof Operations,\n>(\n query: Operation,\n options?: GraphQLQueryOptions,\n) => Promise>;" + }, + "GraphQLQueryOptions": { + "filePath": "src/server/clients/types.ts", + "name": "GraphQLQueryOptions", + "description": "", + "members": [ + { + "filePath": "src/server/clients/types.ts", + "syntaxKind": "PropertySignature", + "name": "variables", + "value": "ApiClientRequestOptions[\"variables\"]", + "description": "The variables to pass to the operation.", + "isOptional": true + }, + { + "filePath": "src/server/clients/types.ts", + "syntaxKind": "PropertySignature", + "name": "apiVersion", + "value": "ApiVersion", + "description": "The version of the API to use for the request.", + "isOptional": true + }, + { + "filePath": "src/server/clients/types.ts", + "syntaxKind": "PropertySignature", + "name": "headers", + "value": "Record", + "description": "Additional headers to include in the request.", + "isOptional": true + }, + { + "filePath": "src/server/clients/types.ts", + "syntaxKind": "PropertySignature", + "name": "tries", + "value": "number", + "description": "The total number of times to try the request if it fails.", + "isOptional": true + } + ], + "value": "export interface GraphQLQueryOptions<\n Operation extends keyof Operations,\n Operations extends AllOperations,\n> {\n /**\n * The variables to pass to the operation.\n */\n variables?: ApiClientRequestOptions['variables'];\n /**\n * The version of the API to use for the request.\n */\n apiVersion?: ApiVersion;\n /**\n * Additional headers to include in the request.\n */\n headers?: Record;\n /**\n * The total number of times to try the request if it fails.\n */\n tries?: number;\n}" + }, + "ApiVersion": { + "filePath": "../../node_modules/@shopify/shopify-api/lib/types.d.ts", + "syntaxKind": "EnumDeclaration", + "name": "ApiVersion", + "value": "export declare enum ApiVersion {\n October22 = \"2022-10\",\n January23 = \"2023-01\",\n April23 = \"2023-04\",\n July23 = \"2023-07\",\n October23 = \"2023-10\",\n January24 = \"2024-01\",\n Unstable = \"unstable\"\n}", + "members": [ + { + "filePath": "../../node_modules/@shopify/shopify-api/lib/types.d.ts", + "name": "October22", + "value": "2022-10" + }, + { + "filePath": "../../node_modules/@shopify/shopify-api/lib/types.d.ts", + "name": "January23", + "value": "2023-01" + }, + { + "filePath": "../../node_modules/@shopify/shopify-api/lib/types.d.ts", + "name": "April23", + "value": "2023-04" + }, + { + "filePath": "../../node_modules/@shopify/shopify-api/lib/types.d.ts", + "name": "July23", + "value": "2023-07" + }, + { + "filePath": "../../node_modules/@shopify/shopify-api/lib/types.d.ts", + "name": "October23", + "value": "2023-10" + }, + { + "filePath": "../../node_modules/@shopify/shopify-api/lib/types.d.ts", + "name": "January24", + "value": "2024-01" + }, + { + "filePath": "../../node_modules/@shopify/shopify-api/lib/types.d.ts", + "name": "Unstable", + "value": "unstable" + } + ] + }, "AdminOperations": { "filePath": "../../node_modules/@shopify/shopify-api/node_modules/@shopify/admin-api-client/dist/ts/graphql/types.d.ts", "syntaxKind": "TypeAliasDeclaration", @@ -3661,11 +4051,25 @@ "title": "app/routes/**\\/.ts" } ] + }, + { + "title": "Handling GraphQL errors", + "description": "Catch `GraphqlQueryError` errors to see error messages from the API.", + "tabs": [ + { + "code": "import { ActionFunctionArgs } from \"@remix-run/node\";\nimport { authenticate } from \"../shopify.server\";\n\nexport const action = async ({ request }: ActionFunctionArgs) => {\n const { storefront } = await authenticate.public.appProxy(request);\n\n try {\n const response = await storefront.graphql(\n `#graphql\n query incorrectQuery {\n products(first: 10) {\n nodes {\n not_a_field\n }\n }\n }`,\n );\n\n return json({ data: await response.json() });\n } catch (error) {\n if (error instanceof GraphqlQueryError) {\n // { errors: { graphQLErrors: [\n // { message: \"Field 'not_a_field' doesn't exist on type 'Product'\" }\n // ] } }\n return json({ errors: error.body?.errors }, { status: 500 });\n }\n return json({ message: \"An error occurred\" }, { status: 500 });\n }\n}", + "title": "/app/routes/**\\/*.ts" + }, + { + "code": "import { shopifyApp } from \"@shopify/shopify-app-remix/server\";\n\nconst shopify = shopifyApp({\n // ...\n});\nexport default shopify;\nexport const authenticate = shopify.authenticate;", + "title": "/app/shopify.server.ts" + } + ] } ] } ], - "value": "export interface StorefrontContext {\n /**\n * Method for interacting with the Shopify Storefront GraphQL API\n *\n * 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).\n *\n * {@link https://shopify.dev/docs/api/storefront}\n *\n * @example\n * Querying the GraphQL API.\n * Use `storefront.graphql` to make query / mutation requests.\n * ```ts\n * // app/routes/**\\/.ts\n * import { json } from \"@remix-run/node\";\n * import { authenticate } from \"../shopify.server\";\n *\n * export async function action({ request }: ActionFunctionArgs) {\n * const { storefront } = await authenticate.public.appProxy(request);\n *\n * const response = await storefront.graphql(`{blogs(first: 10) { edges { node { id } } } }`);\n *\n * return json(await response.json());\n * }\n * ```\n */\n graphql: GraphQLClient;\n}" + "value": "export interface StorefrontContext {\n /**\n * Method for interacting with the Shopify Storefront GraphQL API\n *\n * 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).\n *\n * {@link https://shopify.dev/docs/api/storefront}\n *\n * @example\n * Querying the GraphQL API.\n * Use `storefront.graphql` to make query / mutation requests.\n * ```ts\n * // app/routes/**\\/.ts\n * import { json } from \"@remix-run/node\";\n * import { authenticate } from \"../shopify.server\";\n *\n * export async function action({ request }: ActionFunctionArgs) {\n * const { storefront } = await authenticate.public.appProxy(request);\n *\n * const response = await storefront.graphql(`{blogs(first: 10) { edges { node { id } } } }`);\n *\n * return json(await response.json());\n * }\n * ```\n *\n * @example\n * Handling GraphQL errors.\n * Catch `GraphqlQueryError` errors to see error messages from the API.\n * ```ts\n * // /app/routes/**\\/*.ts\n * import { ActionFunctionArgs } from \"@remix-run/node\";\n * import { authenticate } from \"../shopify.server\";\n *\n * export const action = async ({ request }: ActionFunctionArgs) => {\n * const { storefront } = await authenticate.public.appProxy(request);\n *\n * try {\n * const response = await storefront.graphql(\n * `#graphql\n * query incorrectQuery {\n * products(first: 10) {\n * nodes {\n * not_a_field\n * }\n * }\n * }`,\n * );\n *\n * return json({ data: await response.json() });\n * } catch (error) {\n * if (error instanceof GraphqlQueryError) {\n * // { errors: { graphQLErrors: [\n * // { message: \"Field 'not_a_field' doesn't exist on type 'Product'\" }\n * // ] } }\n * return json({ errors: error.body?.errors }, { status: 500 });\n * }\n * return json({ message: \"An error occurred\" }, { status: 500 });\n * }\n * }\n * ```\n *\n * ```ts\n * // /app/shopify.server.ts\n * import { shopifyApp } from \"@shopify/shopify-app-remix/server\";\n *\n * const shopify = shopifyApp({\n * // ...\n * });\n * export default shopify;\n * export const authenticate = shopify.authenticate;\n * ```\n */\n graphql: GraphQLClient;\n}" }, "StorefrontOperations": { "filePath": "../../node_modules/@shopify/shopify-api/node_modules/@shopify/storefront-api-client/dist/storefront-api-client.d.ts", @@ -3726,6 +4130,12 @@ "name": "Storefront API context", "subtitle": "Interact with the Storefront API.", "url": "/docs/api/shopify-app-remix/apis/storefront-api" + }, + { + "name": "Liquid reference", + "subtitle": "Use the shop's theme to render a template.", + "url": "/docs/api/liquid", + "type": "liquid" } ], "examples": { @@ -3741,7 +4151,7 @@ "tabs": [ { "title": "app/routes/**\\/.ts", - "code": "import { json } from \"@remix-run/node\";\nimport { authenticate } from \"../shopify.server\";\nimport { getMyAppModelData } from \"~/db/model.server\";\n\nexport const loader = async ({ request }) => {\n // Get the session for the shop that initiated the request to the app proxy.\n const { session } = await authenticate.public.appProxy(request);\n\n // Use the session data to make to queries to your database or additional requests.\n return json(await getMyAppModelData({shop: session.shop));\n};", + "code": "import { json } from \"@remix-run/node\";\nimport { authenticate } from \"../shopify.server\";\nimport { getMyAppModelData } from \"~/db/model.server\";\n\nexport const loader = async ({ request }) => {\n // Get the session for the shop that initiated the request to the app proxy.\n const { session } =\n await authenticate.public.appProxy(request);\n\n // Use the session data to make to queries to your database or additional requests.\n return json(\n await getMyAppModelData({shop: session.shop})\n );\n};", "language": "typescript" } ] @@ -3759,7 +4169,7 @@ "tabs": [ { "title": "app/routes/**\\/.ts", - "code": "import { json } from \"@remix-run/node\";\nimport { authenticate } from \"../shopify.server\";\n\nexport async function action({ request }: ActionFunctionArgs) {\n const { admin } = await authenticate.public.appProxy(request);\n\n const response = await admin.graphql(\n `#graphql\n mutation populateProduct($input: ProductInput!) {\n productCreate(input: $input) {\n product {\n id\n }\n }\n }`,\n { variables: { input: { title: \"Product Name\" } } }\n );\n\n const productData = await response.json();\n return json({ data: productData.data });\n}", + "code": "import { json } from \"@remix-run/node\";\nimport { authenticate } from \"../shopify.server\";\n\nexport async function action({ request }: ActionFunctionArgs) {\n const { admin } = await authenticate.public.appProxy(request);\n\n const response = await admin.graphql(\n `#graphql\n mutation populateProduct($input: ProductInput!) {\n productCreate(input: $input) {\n product {\n id\n }\n }\n }`,\n {\n variables: {\n input: { title: \"Product Name\" }\n }\n }\n );\n\n const productData = await response.json();\n return json({ data: productData.data });\n}", "language": "typescript" } ] @@ -3777,7 +4187,7 @@ "tabs": [ { "title": "app/routes/**\\/.ts", - "code": "import { json } from \"@remix-run/node\";\nimport { authenticate } from \"../shopify.server\";\n\nexport async function action({ request }: ActionFunctionArgs) {\n const { storefront } = await authenticate.public.appProxy(request);\n\n const response = await storefront.graphql(`{blogs(first: 10) { edges { node { id } } } }`);\n\n return json(await response.json());\n}", + "code": "import { json } from \"@remix-run/node\";\nimport { authenticate } from \"../shopify.server\";\n\nexport async function action({ request }: ActionFunctionArgs) {\n const { storefront } = await authenticate.public.appProxy(request);\n\n const response = await storefront.graphql(\n `#graphql\n query blogIds {\n blogs(first: 10) {\n edges {\n node {\n id\n }\n }\n }\n }`\n );\n\n return json(await response.json());\n}", "language": "typescript" } ] @@ -3789,7 +4199,7 @@ "title": "liquid", "examples": [ { - "description": "Use the `liquid` helper to render a `Response` with Liquid content using the shop's theme.", + "description": "Use the `liquid` helper to render a `Response` with Liquid content using the shop's theme. See the [Liquid reference](https://shopify.dev/docs/api/liquid) for all the features it enables.", "codeblock": { "title": "Rendering liquid content", "tabs": [ @@ -4118,7 +4528,7 @@ "description": "Get the API version used for webhook request.", "tabs": [ { - "code": "import { ActionFunction } from \"@remix-run/node\";\nimport { authenticate } from \"../shopify.server\";\n\nexport const action: ActionFunction = async ({ request }) => {\n const { apiVersion } = await authenticate.webhook(request);\n return new Response();\n};", + "code": "import { ActionFunctionArgs } from \"@remix-run/node\";\nimport { authenticate } from \"../shopify.server\";\n\nexport const action = async ({ request }: ActionFunctionArgs) => {\n const { apiVersion } = await authenticate.webhook(request);\n return new Response();\n};", "title": "/app/routes/webhooks.tsx" } ] @@ -4137,7 +4547,7 @@ "description": "Get the shop that triggered a webhook.", "tabs": [ { - "code": "import { ActionFunction } from \"@remix-run/node\";\nimport { authenticate } from \"../shopify.server\";\n\nexport const action: ActionFunction = async ({ request }) => {\n const { shop } = await authenticate.webhook(request);\n return new Response();\n};", + "code": "import { ActionFunctionArgs } from \"@remix-run/node\";\nimport { authenticate } from \"../shopify.server\";\n\nexport const action = async ({ request }: ActionFunctionArgs) => {\n const { shop } = await authenticate.webhook(request);\n return new Response();\n};", "title": "/app/routes/webhooks.tsx" } ] @@ -4156,7 +4566,7 @@ "description": "Get the event topic for the webhook.", "tabs": [ { - "code": "import { ActionFunction } from \"@remix-run/node\";\nimport { authenticate } from \"../shopify.server\";\n\nexport const action: ActionFunction = async ({ request }) => {\n const { topic } = await authenticate.webhook(request);\n\n switch (topic) {\n case \"APP_UNINSTALLED\":\n // Do something when the app is uninstalled.\n break;\n }\n\n return new Response();\n};", + "code": "import { ActionFunctionArgs } from \"@remix-run/node\";\nimport { authenticate } from \"../shopify.server\";\n\nexport const action = async ({ request }: ActionFunctionArgs) => {\n const { topic } = await authenticate.webhook(request);\n\n switch (topic) {\n case \"APP_UNINSTALLED\":\n // Do something when the app is uninstalled.\n break;\n }\n\n return new Response();\n};", "title": "/app/routes/webhooks.tsx" } ] @@ -4175,7 +4585,7 @@ "description": "Get the webhook ID.", "tabs": [ { - "code": "import { ActionFunction } from \"@remix-run/node\";\nimport { authenticate } from \"../shopify.server\";\n\nexport const action: ActionFunction = async ({ request }) => {\n const { webhookId } = await authenticate.webhook(request);\n return new Response();\n};", + "code": "import { ActionFunctionArgs } from \"@remix-run/node\";\nimport { authenticate } from \"../shopify.server\";\n\nexport const action = async ({ request }: ActionFunctionArgs) => {\n const { webhookId } = await authenticate.webhook(request);\n return new Response();\n};", "title": "/app/routes/webhooks.tsx" } ] @@ -4194,7 +4604,7 @@ "description": "Get the request's POST payload.", "tabs": [ { - "code": "import { ActionFunction } from \"@remix-run/node\";\nimport { authenticate } from \"../shopify.server\";\n\nexport const action: ActionFunction = async ({ request }) => {\n const { payload } = await authenticate.webhook(request);\n return new Response();\n};", + "code": "import { ActionFunctionArgs } from \"@remix-run/node\";\nimport { authenticate } from \"../shopify.server\";\n\nexport const action = async ({ request }: ActionFunctionArgs) => {\n const { payload } = await authenticate.webhook(request);\n return new Response();\n};", "title": "/app/routes/webhooks.tsx" } ] @@ -4214,7 +4624,7 @@ "description": "Get the webhook sub-topic.", "tabs": [ { - "code": "import { ActionFunction } from \"@remix-run/node\";\nimport { authenticate } from \"../shopify.server\";\n\nexport const action: ActionFunction = async ({ request }) => {\n const { subTopic } = await authenticate.webhook(request);\n return new Response();\n};", + "code": "import { ActionFunctionArgs } from \"@remix-run/node\";\nimport { authenticate } from \"../shopify.server\";\n\nexport const action = async ({ request }: ActionFunctionArgs) => {\n const { subTopic } = await authenticate.webhook(request);\n return new Response();\n};", "title": "/app/routes/webhooks.tsx" } ] @@ -4550,7 +4960,7 @@ "description": "Get the API version used for webhook request.", "tabs": [ { - "code": "import { ActionFunction } from \"@remix-run/node\";\nimport { authenticate } from \"../shopify.server\";\n\nexport const action: ActionFunction = async ({ request }) => {\n const { apiVersion } = await authenticate.webhook(request);\n return new Response();\n};", + "code": "import { ActionFunctionArgs } from \"@remix-run/node\";\nimport { authenticate } from \"../shopify.server\";\n\nexport const action = async ({ request }: ActionFunctionArgs) => {\n const { apiVersion } = await authenticate.webhook(request);\n return new Response();\n};", "title": "/app/routes/webhooks.tsx" } ] @@ -4569,7 +4979,7 @@ "description": "Get the shop that triggered a webhook.", "tabs": [ { - "code": "import { ActionFunction } from \"@remix-run/node\";\nimport { authenticate } from \"../shopify.server\";\n\nexport const action: ActionFunction = async ({ request }) => {\n const { shop } = await authenticate.webhook(request);\n return new Response();\n};", + "code": "import { ActionFunctionArgs } from \"@remix-run/node\";\nimport { authenticate } from \"../shopify.server\";\n\nexport const action = async ({ request }: ActionFunctionArgs) => {\n const { shop } = await authenticate.webhook(request);\n return new Response();\n};", "title": "/app/routes/webhooks.tsx" } ] @@ -4588,7 +4998,7 @@ "description": "Get the event topic for the webhook.", "tabs": [ { - "code": "import { ActionFunction } from \"@remix-run/node\";\nimport { authenticate } from \"../shopify.server\";\n\nexport const action: ActionFunction = async ({ request }) => {\n const { topic } = await authenticate.webhook(request);\n\n switch (topic) {\n case \"APP_UNINSTALLED\":\n // Do something when the app is uninstalled.\n break;\n }\n\n return new Response();\n};", + "code": "import { ActionFunctionArgs } from \"@remix-run/node\";\nimport { authenticate } from \"../shopify.server\";\n\nexport const action = async ({ request }: ActionFunctionArgs) => {\n const { topic } = await authenticate.webhook(request);\n\n switch (topic) {\n case \"APP_UNINSTALLED\":\n // Do something when the app is uninstalled.\n break;\n }\n\n return new Response();\n};", "title": "/app/routes/webhooks.tsx" } ] @@ -4607,7 +5017,7 @@ "description": "Get the webhook ID.", "tabs": [ { - "code": "import { ActionFunction } from \"@remix-run/node\";\nimport { authenticate } from \"../shopify.server\";\n\nexport const action: ActionFunction = async ({ request }) => {\n const { webhookId } = await authenticate.webhook(request);\n return new Response();\n};", + "code": "import { ActionFunctionArgs } from \"@remix-run/node\";\nimport { authenticate } from \"../shopify.server\";\n\nexport const action = async ({ request }: ActionFunctionArgs) => {\n const { webhookId } = await authenticate.webhook(request);\n return new Response();\n};", "title": "/app/routes/webhooks.tsx" } ] @@ -4626,7 +5036,7 @@ "description": "Get the request's POST payload.", "tabs": [ { - "code": "import { ActionFunction } from \"@remix-run/node\";\nimport { authenticate } from \"../shopify.server\";\n\nexport const action: ActionFunction = async ({ request }) => {\n const { payload } = await authenticate.webhook(request);\n return new Response();\n};", + "code": "import { ActionFunctionArgs } from \"@remix-run/node\";\nimport { authenticate } from \"../shopify.server\";\n\nexport const action = async ({ request }: ActionFunctionArgs) => {\n const { payload } = await authenticate.webhook(request);\n return new Response();\n};", "title": "/app/routes/webhooks.tsx" } ] @@ -4646,7 +5056,7 @@ "description": "Get the webhook sub-topic.", "tabs": [ { - "code": "import { ActionFunction } from \"@remix-run/node\";\nimport { authenticate } from \"../shopify.server\";\n\nexport const action: ActionFunction = async ({ request }) => {\n const { subTopic } = await authenticate.webhook(request);\n return new Response();\n};", + "code": "import { ActionFunctionArgs } from \"@remix-run/node\";\nimport { authenticate } from \"../shopify.server\";\n\nexport const action = async ({ request }: ActionFunctionArgs) => {\n const { subTopic } = await authenticate.webhook(request);\n return new Response();\n};", "title": "/app/routes/webhooks.tsx" } ] @@ -4925,13 +5335,13 @@ "title": "Using offline sessions", "description": "Get your app's shop-specific data using an offline session.", "tabs": [ - { - "code": "import { shopifyApp } from \"@shopify/shopify-app-remix/server\";\n\nconst shopify = shopifyApp({\n // ...etc\n});\nexport default shopify;\nexport const authenticate = shopify.authenticate;", - "title": "shopify.server.ts" - }, { "code": "import { LoaderFunctionArgs, json } from \"@remix-run/node\";\nimport { authenticate } from \"../shopify.server\";\nimport { getMyAppData } from \"~/db/model.server\";\n\nexport const loader = async ({ request }: LoaderFunctionArgs) => {\n const { session } = await authenticate.admin(request);\n return json(await getMyAppData({shop: session.shop));\n};", "title": "/app/routes/**\\/*.ts" + }, + { + "code": "import { shopifyApp } from \"@shopify/shopify-app-remix/server\";\n\nconst shopify = shopifyApp({\n // ...etc\n});\nexport default shopify;\nexport const authenticate = shopify.authenticate;", + "title": "shopify.server.ts" } ] }, @@ -4939,13 +5349,13 @@ "title": "Using online sessions", "description": "Get your app's user-specific data using an online session.", "tabs": [ - { - "code": "import { shopifyApp } from \"@shopify/shopify-app-remix/server\";\n\nconst shopify = shopifyApp({\n // ...etc\n useOnlineTokens: true,\n});\nexport default shopify;\nexport const authenticate = shopify.authenticate;", - "title": "shopify.server.ts" - }, { "code": "import { LoaderFunctionArgs, json } from \"@remix-run/node\";\nimport { authenticate } from \"../shopify.server\";\nimport { getMyAppData } from \"~/db/model.server\";\n\nexport const loader = async ({ request }: LoaderFunctionArgs) => {\n const { session } = await authenticate.admin(request);\n return json(await getMyAppData({user: session.onlineAccessInfo!.id}));\n};", "title": "/app/routes/**\\/*.ts" + }, + { + "code": "import { shopifyApp } from \"@shopify/shopify-app-remix/server\";\n\nconst shopify = shopifyApp({\n // ...etc\n useOnlineTokens: true,\n});\nexport default shopify;\nexport const authenticate = shopify.authenticate;", + "title": "shopify.server.ts" } ] } @@ -5004,7 +5414,7 @@ "description": "Getting the number of orders in a store using REST resources. Visit the [Admin REST API references](/docs/api/admin-rest) for examples on using each resource.", "tabs": [ { - "code": "import { LoaderFunctionArgs, json } from \"@remix-run/node\";\nimport { authenticate } from \"../shopify.server\";\n\nexport const loader = async ({ request }: LoaderFunctionArgs) => {\n const { admin, session } = await authenticate.admin(request);\n return json(admin.rest.resources.Order.count({ session }));\n};", + "code": "import { LoaderFunctionArgs, json } from \"@remix-run/node\";\nimport { authenticate } from \"../shopify.server\";\n\nexport const loader = async ({ request }: LoaderFunctionArgs) => {\n const {\n admin,\n session,\n } = await authenticate.admin(request);\n\n return json(\n admin.rest.resources.Order.count({ session }),\n );\n};", "title": "/app/routes/**\\/*.ts" }, { @@ -5018,7 +5428,7 @@ "description": "Use `admin.rest.get` to make custom requests to make a request to to the `customer/count` endpoint", "tabs": [ { - "code": "import { LoaderFunctionArgs, json } from \"@remix-run/node\";\nimport { authenticate } from \"../shopify.server\";\n\nexport const loader = async ({ request }: LoaderFunctionArgs) => {\n const { admin, session } = await authenticate.admin(request);\n const response = await admin.rest.get({ path: \"/customers/count.json\" });\n const customers = await response.json();\n return json({ customers });\n};", + "code": "import { LoaderFunctionArgs, json } from \"@remix-run/node\";\nimport { authenticate } from \"../shopify.server\";\n\nexport const loader = async ({ request }: LoaderFunctionArgs) => {\n const {\n admin,\n session,\n } = await authenticate.admin(request);\n\n const response = await admin.rest.get({\n path: \"/customers/count.json\",\n });\n const customers = await response.json();\n\n return json({ customers });\n};", "title": "/app/routes/**\\/*.ts" }, { @@ -5032,7 +5442,7 @@ "description": "Use `admin.rest.post` to make custom requests to make a request to to the `customers.json` endpoint to send a welcome email", "tabs": [ { - "code": "import { LoaderFunctionArgs, json } from \"@remix-run/node\";\nimport { authenticate } from \"../shopify.server\";\n\nexport const loader = async ({ request }: LoaderFunctionArgs) => {\n const { admin, session } = await authenticate.admin(request);\n const response = admin.rest.post({\n path: \"customers/7392136888625/send_invite.json\",\n body: {\n customer_invite: {\n to: \"new_test_email@shopify.com\",\n from: \"j.limited@example.com\",\n bcc: [\"j.limited@example.com\"],\n subject: \"Welcome to my new shop\",\n custom_message: \"My awesome new store\",\n },\n },\n});\n const customerInvite = await response.json();\n return json({ customerInvite });\n};", + "code": "import { LoaderFunctionArgs, json } from \"@remix-run/node\";\nimport { authenticate } from \"../shopify.server\";\n\nexport const loader = async ({ request }: LoaderFunctionArgs) => {\n const {\n admin,\n session,\n } = await authenticate.admin(request);\n\n const response = admin.rest.post({\n path: \"customers/7392136888625/send_invite.json\",\n body: {\n customer_invite: {\n to: \"new_test_email@shopify.com\",\n from: \"j.limited@example.com\",\n bcc: [\"j.limited@example.com\"],\n subject: \"Welcome to my new shop\",\n custom_message: \"My awesome new store\",\n },\n },\n });\n\n const customerInvite = await response.json();\n return json({ customerInvite });\n};", "title": "/app/routes/**\\/*.ts" }, { @@ -5055,11 +5465,25 @@ "description": "Use `admin.graphql` to make query / mutation requests.", "tabs": [ { - "code": "import { ActionFunctionArgs } from \"@remix-run/node\";\nimport { authenticate } from \"../shopify.server\";\n\nexport async function action({ request }: ActionFunctionArgs) {\n const { admin } = await authenticate.admin(request);\n\n const response = await admin.graphql(\n `#graphql\n mutation populateProduct($input: ProductInput!) {\n productCreate(input: $input) {\n product {\n id\n }\n }\n }`,\n { variables: { input: { title: \"Product Name\" } } }\n );\n\n const productData = await response.json();\n return json({ data: productData.data });\n}", - "title": "Example" + "code": "import { ActionFunctionArgs } from \"@remix-run/node\";\nimport { authenticate } from \"../shopify.server\";\n\nexport const action = async ({ request }: ActionFunctionArgs) => {\n const { admin } = await authenticate.admin(request);\n\n const response = await admin.graphql(\n `#graphql\n mutation populateProduct($input: ProductInput!) {\n productCreate(input: $input) {\n product {\n id\n }\n }\n }`,\n {\n variables: {\n input: { title: \"Product Name\" },\n },\n },\n );\n\n const productData = await response.json();\n return json({\n productId: productData.data?.productCreate?.product?.id,\n });\n}", + "title": "/app/routes/**\\/*.ts" + }, + { + "code": "import { shopifyApp } from \"@shopify/shopify-app-remix/server\";\n\nconst shopify = shopifyApp({\n // ...\n});\nexport default shopify;\nexport const authenticate = shopify.authenticate;", + "title": "/app/shopify.server.ts" + } + ] + }, + { + "title": "Handling GraphQL errors", + "description": "Catch `GraphqlQueryError` errors to see error messages from the API.", + "tabs": [ + { + "code": "import { ActionFunctionArgs } from \"@remix-run/node\";\nimport { authenticate } from \"../shopify.server\";\n\nexport const action = async ({ request }: ActionFunctionArgs) => {\n const { admin } = await authenticate.admin(request);\n\n try {\n const response = await admin.graphql(\n `#graphql\n query incorrectQuery {\n products(first: 10) {\n nodes {\n not_a_field\n }\n }\n }`,\n );\n\n return json({ data: await response.json() });\n } catch (error) {\n if (error instanceof GraphqlQueryError) {\n // error.body.errors:\n // { graphQLErrors: [\n // { message: \"Field 'not_a_field' doesn't exist on type 'Product'\" }\n // ] }\n return json({ errors: error.body?.errors }, { status: 500 });\n }\n return json({ message: \"An error occurred\" }, { status: 500 });\n }\n}", + "title": "/app/routes/**\\/*.ts" }, { - "code": "import { shopifyApp } from \"@shopify/shopify-app-remix/server\";\n\nconst shopify = shopifyApp({\n restResources,\n // ...etc\n});\nexport default shopify;\nexport const unauthenticated = shopify.unauthenticated;", + "code": "import { shopifyApp } from \"@shopify/shopify-app-remix/server\";\n\nconst shopify = shopifyApp({\n // ...\n});\nexport default shopify;\nexport const authenticate = shopify.authenticate;", "title": "/app/shopify.server.ts" } ] @@ -5067,7 +5491,7 @@ ] } ], - "value": "export interface AdminApiContext<\n Resources extends ShopifyRestResources = ShopifyRestResources,\n> {\n /**\n * Methods for interacting with the Shopify Admin REST API\n *\n * There are methods for interacting with individual REST resources. You can also make `GET`, `POST`, `PUT` and `DELETE` requests should the REST resources not meet your needs.\n *\n * {@link https://shopify.dev/docs/api/admin-rest}\n *\n * @example\n * Using REST resources.\n * Getting the number of orders in a store using REST resources. Visit the [Admin REST API references](/docs/api/admin-rest) for examples on using each resource. \n *\n * ```ts\n * // /app/routes/**\\/*.ts\n * import { LoaderFunctionArgs, json } from \"@remix-run/node\";\n * import { authenticate } from \"../shopify.server\";\n *\n * export const loader = async ({ request }: LoaderFunctionArgs) => {\n * const { admin, session } = await authenticate.admin(request);\n * return json(admin.rest.resources.Order.count({ session }));\n * };\n * ```\n *\n *\n * ```ts\n * // /app/shopify.server.ts\n * import { shopifyApp } from \"@shopify/shopify-app-remix/server\";\n * import { restResources } from \"@shopify/shopify-api/rest/admin/2023-07\";\n *\n * const shopify = shopifyApp({\n * restResources,\n * // ...etc\n * });\n * export default shopify;\n * export const authenticate = shopify.authenticate;\n * ```\n *\n *\n * @example\n * Performing a GET request to the REST API.\n * Use `admin.rest.get` to make custom requests to make a request to to the `customer/count` endpoint\n *\n * ```ts\n * // /app/routes/**\\/*.ts\n * import { LoaderFunctionArgs, json } from \"@remix-run/node\";\n * import { authenticate } from \"../shopify.server\";\n *\n * export const loader = async ({ request }: LoaderFunctionArgs) => {\n * const { admin, session } = await authenticate.admin(request);\n * const response = await admin.rest.get({ path: \"/customers/count.json\" });\n * const customers = await response.json();\n * return json({ customers });\n * };\n * ```\n *\n * ```ts\n * // /app/shopify.server.ts\n * import { shopifyApp } from \"@shopify/shopify-app-remix/server\";\n * import { restResources } from \"@shopify/shopify-api/rest/admin/2023-04\";\n *\n * const shopify = shopifyApp({\n * restResources,\n * // ...etc\n * });\n * export default shopify;\n * export const authenticate = shopify.authenticate;\n * ```\n * @example\n * Performing a POST request to the REST API.\n * Use `admin.rest.post` to make custom requests to make a request to to the `customers.json` endpoint to send a welcome email\n * ```ts\n * // /app/routes/**\\/*.ts\n * import { LoaderFunctionArgs, json } from \"@remix-run/node\";\n * import { authenticate } from \"../shopify.server\";\n *\n * export const loader = async ({ request }: LoaderFunctionArgs) => {\n * const { admin, session } = await authenticate.admin(request);\n * const response = admin.rest.post({\n * path: \"customers/7392136888625/send_invite.json\",\n * body: {\n * customer_invite: {\n * to: \"new_test_email@shopify.com\",\n * from: \"j.limited@example.com\",\n * bcc: [\"j.limited@example.com\"],\n * subject: \"Welcome to my new shop\",\n * custom_message: \"My awesome new store\",\n * },\n * },\n * });\n * const customerInvite = await response.json();\n * return json({ customerInvite });\n * };\n * ```\n *\n * ```ts\n * // /app/shopify.server.ts\n * import { shopifyApp } from \"@shopify/shopify-app-remix/server\";\n * import { restResources } from \"@shopify/shopify-api/rest/admin/2023-04\";\n *\n * const shopify = shopifyApp({\n * restResources,\n * // ...etc\n * });\n * export default shopify;\n * export const authenticate = shopify.authenticate;\n * ```\n */\n rest: RestClientWithResources;\n\n /**\n * Methods for interacting with the Shopify Admin GraphQL API\n *\n * {@link https://shopify.dev/docs/api/admin-graphql}\n * {@link https://github.com/Shopify/shopify-api-js/blob/main/packages/shopify-api/docs/reference/clients/Graphql.md}\n *\n * @example\n * Querying the GraphQL API.\n * Use `admin.graphql` to make query / mutation requests.\n * ```ts\n * import { ActionFunctionArgs } from \"@remix-run/node\";\n * import { authenticate } from \"../shopify.server\";\n *\n * export async function action({ request }: ActionFunctionArgs) {\n * const { admin } = await authenticate.admin(request);\n *\n * const response = await admin.graphql(\n * `#graphql\n * mutation populateProduct($input: ProductInput!) {\n * productCreate(input: $input) {\n * product {\n * id\n * }\n * }\n * }`,\n * { variables: { input: { title: \"Product Name\" } } }\n * );\n *\n * const productData = await response.json();\n * return json({ data: productData.data });\n * }\n * ```\n *\n * ```ts\n * // /app/shopify.server.ts\n * import { shopifyApp } from \"@shopify/shopify-app-remix/server\";\n *\n * const shopify = shopifyApp({\n * restResources,\n * // ...etc\n * });\n * export default shopify;\n * export const unauthenticated = shopify.unauthenticated;\n * ```\n */\n graphql: GraphQLClient;\n}" + "value": "export interface AdminApiContext<\n Resources extends ShopifyRestResources = ShopifyRestResources,\n> {\n /**\n * Methods for interacting with the Shopify Admin REST API\n *\n * There are methods for interacting with individual REST resources. You can also make `GET`, `POST`, `PUT` and `DELETE` requests should the REST resources not meet your needs.\n *\n * {@link https://shopify.dev/docs/api/admin-rest}\n *\n * @example\n * Using REST resources.\n * Getting the number of orders in a store using REST resources. Visit the [Admin REST API references](/docs/api/admin-rest) for examples on using each resource. \n *\n * ```ts\n * // /app/routes/**\\/*.ts\n * import { LoaderFunctionArgs, json } from \"@remix-run/node\";\n * import { authenticate } from \"../shopify.server\";\n *\n * export const loader = async ({ request }: LoaderFunctionArgs) => {\n * const {\n * admin,\n * session,\n * } = await authenticate.admin(request);\n *\n * return json(\n * admin.rest.resources.Order.count({ session }),\n * );\n * };\n * ```\n *\n * ```ts\n * // /app/shopify.server.ts\n * import { shopifyApp } from \"@shopify/shopify-app-remix/server\";\n * import { restResources } from \"@shopify/shopify-api/rest/admin/2023-07\";\n *\n * const shopify = shopifyApp({\n * restResources,\n * // ...etc\n * });\n * export default shopify;\n * export const authenticate = shopify.authenticate;\n * ```\n *\n * @example\n * Performing a GET request to the REST API.\n * Use `admin.rest.get` to make custom requests to make a request to to the `customer/count` endpoint\n *\n * ```ts\n * // /app/routes/**\\/*.ts\n * import { LoaderFunctionArgs, json } from \"@remix-run/node\";\n * import { authenticate } from \"../shopify.server\";\n *\n * export const loader = async ({ request }: LoaderFunctionArgs) => {\n * const {\n * admin,\n * session,\n * } = await authenticate.admin(request);\n *\n * const response = await admin.rest.get({\n * path: \"/customers/count.json\",\n * });\n * const customers = await response.json();\n *\n * return json({ customers });\n * };\n * ```\n *\n * ```ts\n * // /app/shopify.server.ts\n * import { shopifyApp } from \"@shopify/shopify-app-remix/server\";\n * import { restResources } from \"@shopify/shopify-api/rest/admin/2023-04\";\n *\n * const shopify = shopifyApp({\n * restResources,\n * // ...etc\n * });\n * export default shopify;\n * export const authenticate = shopify.authenticate;\n * ```\n *\n * @example\n * Performing a POST request to the REST API.\n * Use `admin.rest.post` to make custom requests to make a request to to the `customers.json` endpoint to send a welcome email\n * ```ts\n * // /app/routes/**\\/*.ts\n * import { LoaderFunctionArgs, json } from \"@remix-run/node\";\n * import { authenticate } from \"../shopify.server\";\n *\n * export const loader = async ({ request }: LoaderFunctionArgs) => {\n * const {\n * admin,\n * session,\n * } = await authenticate.admin(request);\n *\n * const response = admin.rest.post({\n * path: \"customers/7392136888625/send_invite.json\",\n * body: {\n * customer_invite: {\n * to: \"new_test_email@shopify.com\",\n * from: \"j.limited@example.com\",\n * bcc: [\"j.limited@example.com\"],\n * subject: \"Welcome to my new shop\",\n * custom_message: \"My awesome new store\",\n * },\n * },\n * });\n *\n * const customerInvite = await response.json();\n * return json({ customerInvite });\n * };\n * ```\n *\n * ```ts\n * // /app/shopify.server.ts\n * import { shopifyApp } from \"@shopify/shopify-app-remix/server\";\n * import { restResources } from \"@shopify/shopify-api/rest/admin/2023-04\";\n *\n * const shopify = shopifyApp({\n * restResources,\n * // ...etc\n * });\n * export default shopify;\n * export const authenticate = shopify.authenticate;\n * ```\n */\n rest: RestClientWithResources;\n\n /**\n * Methods for interacting with the Shopify Admin GraphQL API\n *\n * {@link https://shopify.dev/docs/api/admin-graphql}\n * {@link https://github.com/Shopify/shopify-api-js/blob/main/packages/shopify-api/docs/reference/clients/Graphql.md}\n *\n * @example\n * Querying the GraphQL API.\n * Use `admin.graphql` to make query / mutation requests.\n * ```ts\n * // /app/routes/**\\/*.ts\n * import { ActionFunctionArgs } from \"@remix-run/node\";\n * import { authenticate } from \"../shopify.server\";\n *\n * export const action = async ({ request }: ActionFunctionArgs) => {\n * const { admin } = await authenticate.admin(request);\n *\n * const response = await admin.graphql(\n * `#graphql\n * mutation populateProduct($input: ProductInput!) {\n * productCreate(input: $input) {\n * product {\n * id\n * }\n * }\n * }`,\n * {\n * variables: {\n * input: { title: \"Product Name\" },\n * },\n * },\n * );\n *\n * const productData = await response.json();\n * return json({\n * productId: productData.data?.productCreate?.product?.id,\n * });\n * }\n * ```\n *\n * ```ts\n * // /app/shopify.server.ts\n * import { shopifyApp } from \"@shopify/shopify-app-remix/server\";\n *\n * const shopify = shopifyApp({\n * // ...\n * });\n * export default shopify;\n * export const authenticate = shopify.authenticate;\n * ```\n *\n * @example\n * Handling GraphQL errors.\n * Catch `GraphqlQueryError` errors to see error messages from the API.\n * ```ts\n * // /app/routes/**\\/*.ts\n * import { ActionFunctionArgs } from \"@remix-run/node\";\n * import { authenticate } from \"../shopify.server\";\n *\n * export const action = async ({ request }: ActionFunctionArgs) => {\n * const { admin } = await authenticate.admin(request);\n *\n * try {\n * const response = await admin.graphql(\n * `#graphql\n * query incorrectQuery {\n * products(first: 10) {\n * nodes {\n * not_a_field\n * }\n * }\n * }`,\n * );\n *\n * return json({ data: await response.json() });\n * } catch (error) {\n * if (error instanceof GraphqlQueryError) {\n * // error.body.errors:\n * // { graphQLErrors: [\n * // { message: \"Field 'not_a_field' doesn't exist on type 'Product'\" }\n * // ] }\n * return json({ errors: error.body?.errors }, { status: 500 });\n * }\n * return json({ message: \"An error occurred\" }, { status: 500 });\n * }\n * }\n * ```\n *\n * ```ts\n * // /app/shopify.server.ts\n * import { shopifyApp } from \"@shopify/shopify-app-remix/server\";\n *\n * const shopify = shopifyApp({\n * // ...\n * });\n * export default shopify;\n * export const authenticate = shopify.authenticate;\n * ```\n */\n graphql: GraphQLClient;\n}" }, "RestClientWithResources": { "filePath": "src/server/clients/admin/rest.ts", @@ -5220,25 +5644,135 @@ "value": "GetRequestParams & {\n data: Record | string;\n}", "description": "" }, - "AdminOperations": { - "filePath": "../../node_modules/@shopify/shopify-api/node_modules/@shopify/admin-api-client/dist/ts/graphql/types.d.ts", - "syntaxKind": "TypeAliasDeclaration", - "name": "AdminOperations", - "value": "AdminQueries & AdminMutations", - "description": "" - }, - "AdminQueries": { - "filePath": "../../node_modules/@shopify/shopify-api/node_modules/@shopify/admin-api-client/dist/ts/graphql/types.d.ts", - "name": "AdminQueries", + "GraphQLClient": { + "filePath": "src/server/clients/types.ts", + "name": "GraphQLClient", "description": "", - "members": [ + "params": [ { - "filePath": "../../node_modules/@shopify/shopify-api/node_modules/@shopify/admin-api-client/dist/ts/graphql/types.d.ts", - "name": "[key: string]", - "value": "{\n variables: any;\n return: any;\n }" + "name": "query", + "description": "", + "value": "Operation extends keyof Operations", + "filePath": "src/server/clients/types.ts" }, { - "filePath": "../../node_modules/@shopify/shopify-api/node_modules/@shopify/admin-api-client/dist/ts/graphql/types.d.ts", + "name": "options", + "description": "", + "value": "GraphQLQueryOptions", + "isOptional": true, + "filePath": "src/server/clients/types.ts" + } + ], + "returns": { + "filePath": "src/server/clients/types.ts", + "description": "", + "name": "interface Promise {\n /**\n * Attaches callbacks for the resolution and/or rejection of the Promise.\n * @param onfulfilled The callback to execute when the Promise is resolved.\n * @param onrejected The callback to execute when the Promise is rejected.\n * @returns A Promise for the completion of which ever callback is executed.\n */\n then(onfulfilled?: ((value: T) => TResult1 | PromiseLike) | undefined | null, onrejected?: ((reason: any) => TResult2 | PromiseLike) | undefined | null): Promise;\n\n /**\n * Attaches a callback for only the rejection of the Promise.\n * @param onrejected The callback to execute when the Promise is rejected.\n * @returns A Promise for the completion of the callback.\n */\n catch(onrejected?: ((reason: any) => TResult | PromiseLike) | undefined | null): Promise;\n}, interface Promise {}, Promise: PromiseConstructor, interface Promise {\n readonly [Symbol.toStringTag]: string;\n}, interface Promise {\n /**\n * Attaches a callback that is invoked when the Promise is settled (fulfilled or rejected). The\n * resolved value cannot be modified from the callback.\n * @param onfinally The callback to execute when the Promise is settled (fulfilled or rejected).\n * @returns A Promise for the completion of the callback.\n */\n finally(onfinally?: (() => void) | undefined | null): Promise;\n}", + "value": "interface Promise {\n /**\n * Attaches callbacks for the resolution and/or rejection of the Promise.\n * @param onfulfilled The callback to execute when the Promise is resolved.\n * @param onrejected The callback to execute when the Promise is rejected.\n * @returns A Promise for the completion of which ever callback is executed.\n */\n then(onfulfilled?: ((value: T) => TResult1 | PromiseLike) | undefined | null, onrejected?: ((reason: any) => TResult2 | PromiseLike) | undefined | null): Promise;\n\n /**\n * Attaches a callback for only the rejection of the Promise.\n * @param onrejected The callback to execute when the Promise is rejected.\n * @returns A Promise for the completion of the callback.\n */\n catch(onrejected?: ((reason: any) => TResult | PromiseLike) | undefined | null): Promise;\n}, interface Promise {}, Promise: PromiseConstructor, interface Promise {\n readonly [Symbol.toStringTag]: string;\n}, interface Promise {\n /**\n * Attaches a callback that is invoked when the Promise is settled (fulfilled or rejected). The\n * resolved value cannot be modified from the callback.\n * @param onfinally The callback to execute when the Promise is settled (fulfilled or rejected).\n * @returns A Promise for the completion of the callback.\n */\n finally(onfinally?: (() => void) | undefined | null): Promise;\n}" + }, + "value": "export type GraphQLClient = <\n Operation extends keyof Operations,\n>(\n query: Operation,\n options?: GraphQLQueryOptions,\n) => Promise>;" + }, + "GraphQLQueryOptions": { + "filePath": "src/server/clients/types.ts", + "name": "GraphQLQueryOptions", + "description": "", + "members": [ + { + "filePath": "src/server/clients/types.ts", + "syntaxKind": "PropertySignature", + "name": "variables", + "value": "ApiClientRequestOptions[\"variables\"]", + "description": "The variables to pass to the operation.", + "isOptional": true + }, + { + "filePath": "src/server/clients/types.ts", + "syntaxKind": "PropertySignature", + "name": "apiVersion", + "value": "ApiVersion", + "description": "The version of the API to use for the request.", + "isOptional": true + }, + { + "filePath": "src/server/clients/types.ts", + "syntaxKind": "PropertySignature", + "name": "headers", + "value": "Record", + "description": "Additional headers to include in the request.", + "isOptional": true + }, + { + "filePath": "src/server/clients/types.ts", + "syntaxKind": "PropertySignature", + "name": "tries", + "value": "number", + "description": "The total number of times to try the request if it fails.", + "isOptional": true + } + ], + "value": "export interface GraphQLQueryOptions<\n Operation extends keyof Operations,\n Operations extends AllOperations,\n> {\n /**\n * The variables to pass to the operation.\n */\n variables?: ApiClientRequestOptions['variables'];\n /**\n * The version of the API to use for the request.\n */\n apiVersion?: ApiVersion;\n /**\n * Additional headers to include in the request.\n */\n headers?: Record;\n /**\n * The total number of times to try the request if it fails.\n */\n tries?: number;\n}" + }, + "ApiVersion": { + "filePath": "../../node_modules/@shopify/shopify-api/lib/types.d.ts", + "syntaxKind": "EnumDeclaration", + "name": "ApiVersion", + "value": "export declare enum ApiVersion {\n October22 = \"2022-10\",\n January23 = \"2023-01\",\n April23 = \"2023-04\",\n July23 = \"2023-07\",\n October23 = \"2023-10\",\n January24 = \"2024-01\",\n Unstable = \"unstable\"\n}", + "members": [ + { + "filePath": "../../node_modules/@shopify/shopify-api/lib/types.d.ts", + "name": "October22", + "value": "2022-10" + }, + { + "filePath": "../../node_modules/@shopify/shopify-api/lib/types.d.ts", + "name": "January23", + "value": "2023-01" + }, + { + "filePath": "../../node_modules/@shopify/shopify-api/lib/types.d.ts", + "name": "April23", + "value": "2023-04" + }, + { + "filePath": "../../node_modules/@shopify/shopify-api/lib/types.d.ts", + "name": "July23", + "value": "2023-07" + }, + { + "filePath": "../../node_modules/@shopify/shopify-api/lib/types.d.ts", + "name": "October23", + "value": "2023-10" + }, + { + "filePath": "../../node_modules/@shopify/shopify-api/lib/types.d.ts", + "name": "January24", + "value": "2024-01" + }, + { + "filePath": "../../node_modules/@shopify/shopify-api/lib/types.d.ts", + "name": "Unstable", + "value": "unstable" + } + ] + }, + "AdminOperations": { + "filePath": "../../node_modules/@shopify/shopify-api/node_modules/@shopify/admin-api-client/dist/ts/graphql/types.d.ts", + "syntaxKind": "TypeAliasDeclaration", + "name": "AdminOperations", + "value": "AdminQueries & AdminMutations", + "description": "" + }, + "AdminQueries": { + "filePath": "../../node_modules/@shopify/shopify-api/node_modules/@shopify/admin-api-client/dist/ts/graphql/types.d.ts", + "name": "AdminQueries", + "description": "", + "members": [ + { + "filePath": "../../node_modules/@shopify/shopify-api/node_modules/@shopify/admin-api-client/dist/ts/graphql/types.d.ts", + "name": "[key: string]", + "value": "{\n variables: any;\n return: any;\n }" + }, + { + "filePath": "../../node_modules/@shopify/shopify-api/node_modules/@shopify/admin-api-client/dist/ts/graphql/types.d.ts", "name": "[key: number | symbol]", "value": "never" } @@ -5312,7 +5846,7 @@ "title": "/app/routes/**\\/*.ts" }, { - "code": "import { shopifyApp, BillingInterval } from \"@shopify/shopify-app-remix/server\";\n\nexport const MONTHLY_PLAN = 'Monthly subscription';\nexport const ANNUAL_PLAN = 'Annual subscription';\n\nconst shopify = shopifyApp({\n // ...etc\n billing: {\n [MONTHLY_PLAN]: {\n lineItems: [\n {\n amount: 5,\n currencyCode: 'USD',\n interval: BillingInterval.Every30Days,\n }\n {\n amount: 1,\n currencyCode: 'USD',\n interval: BillingInterval.Usage.\n terms: '1 dollar per 1000 emails',\n },\n ],\n },\n }\n future: {v3_lineItemBilling: true}\n});\nexport default shopify;\nexport const authenticate = shopify.authenticate;", + "code": "import { shopifyApp, BillingInterval } from \"@shopify/shopify-app-remix/server\";\n\nexport const MONTHLY_PLAN = 'Monthly subscription';\nexport const ANNUAL_PLAN = 'Annual subscription';\n\nconst shopify = shopifyApp({\n // ...etc\n billing: {\n [MONTHLY_PLAN]: {\n lineItems: [\n {\n amount: 5,\n currencyCode: 'USD',\n interval: BillingInterval.Every30Days,\n },\n {\n amount: 1,\n currencyCode: 'USD',\n interval: BillingInterval.Usage.\n terms: '1 dollar per 1000 emails',\n },\n ],\n },\n }\n future: {v3_lineItemBilling: true}\n});\nexport default shopify;\nexport const authenticate = shopify.authenticate;", "title": "shopify.server.ts" } ] @@ -5389,7 +5923,7 @@ ] } ], - "value": "export interface BillingContext {\n /**\n * Checks if the shop has an active payment for any plan defined in the `billing` config option.\n *\n * @returns A promise that resolves to an object containing the active purchases for the shop.\n *\n * @example\n * Requesting billing right away.\n * Call `billing.request` in the `onFailure` callback to immediately redirect to the Shopify page to request payment.\n * ```ts\n * // /app/routes/**\\/*.ts\n * import { LoaderFunctionArgs } from \"@remix-run/node\";\n * import { authenticate, MONTHLY_PLAN } from \"../shopify.server\";\n *\n * export const loader = async ({ request }: LoaderFunctionArgs) => {\n * const { billing } = await authenticate.admin(request);\n * await billing.require({\n * plans: [MONTHLY_PLAN],\n * isTest: true,\n * onFailure: async () => billing.request({ plan: MONTHLY_PLAN }),\n * });\n *\n * // App logic\n * };\n * ```\n * ```ts\n * // shopify.server.ts\n * import { shopifyApp, BillingInterval } from \"@shopify/shopify-app-remix/server\";\n *\n * export const MONTHLY_PLAN = 'Monthly subscription';\n * export const ANNUAL_PLAN = 'Annual subscription';\n *\n * const shopify = shopifyApp({\n * // ...etc\n * billing: {\n * [MONTHLY_PLAN]: {\n * amount: 5,\n * currencyCode: 'USD',\n * interval: BillingInterval.Every30Days,\n * },\n * [ANNUAL_PLAN]: {\n * amount: 50,\n * currencyCode: 'USD',\n * interval: BillingInterval.Annual,\n * },\n * }\n * });\n * export default shopify;\n * export const authenticate = shopify.authenticate;\n * ```\n *\n * @example\n * Redirect to a plan selection page.\n * When the app has multiple plans, create a page in your App that allows the merchant to select a plan. If a merchant does not have the required plan you can redirect them to page in your app to select one.\n * ```ts\n * // /app/routes/**\\/*.ts\n * import { LoaderFunctionArgs, redirect } from \"@remix-run/node\";\n * import { authenticate, MONTHLY_PLAN, ANNUAL_PLAN } from \"../shopify.server\";\n *\n * export const loader = async ({ request }: LoaderFunctionArgs) => {\n * const { billing } = await authenticate.admin(request);\n * const billingCheck = await billing.require({\n * plans: [MONTHLY_PLAN, ANNUAL_PLAN],\n * isTest: true,\n * onFailure: () => redirect('/select-plan'),\n * });\n *\n * const subscription = billingCheck.appSubscriptions[0];\n * console.log(`Shop is on ${subscription.name} (id ${subscription.id})`);\n *\n * // App logic\n * };\n * ```\n * ```ts\n * // shopify.server.ts\n * import { shopifyApp, BillingInterval } from \"@shopify/shopify-app-remix/server\";\n *\n * export const MONTHLY_PLAN = 'Monthly subscription';\n * export const ANNUAL_PLAN = 'Annual subscription';\n *\n * const shopify = shopifyApp({\n * // ...etc\n * billing: {\n * [MONTHLY_PLAN]: {\n * amount: 5,\n * currencyCode: 'USD',\n * interval: BillingInterval.Every30Days,\n * },\n * [ANNUAL_PLAN]: {\n * amount: 50,\n * currencyCode: 'USD',\n * interval: BillingInterval.Annual,\n * },\n * }\n * });\n * export default shopify;\n * export const authenticate = shopify.authenticate;\n * ```\n * @example\n * Requesting billing with line items\n * Call `billing.request` with the `v3_lineItemBilling` future flag enabled\n * ```ts\n * // /app/routes/**\\/*.ts\n * import { LoaderFunctionArgs } from \"@remix-run/node\";\n * import { authenticate, MONTHLY_PLAN } from \"../shopify.server\";\n *\n * export const loader = async ({ request }: LoaderFunctionArgs) => {\n * const { billing } = await authenticate.admin(request);\n * await billing.require({\n * plans: [MONTHLY_PLAN],\n * isTest: true,\n * onFailure: async () => billing.request({ plan: MONTHLY_PLAN }),\n * });\n *\n * // App logic\n * };\n * ```\n * ```ts\n * // shopify.server.ts\n * import { shopifyApp, BillingInterval } from \"@shopify/shopify-app-remix/server\";\n *\n * export const MONTHLY_PLAN = 'Monthly subscription';\n * export const ANNUAL_PLAN = 'Annual subscription';\n *\n * const shopify = shopifyApp({\n * // ...etc\n * billing: {\n * [MONTHLY_PLAN]: {\n * lineItems: [\n * {\n * amount: 5,\n * currencyCode: 'USD',\n * interval: BillingInterval.Every30Days,\n * }\n * {\n * amount: 1,\n * currencyCode: 'USD',\n * interval: BillingInterval.Usage.\n * terms: '1 dollar per 1000 emails',\n * },\n * ],\n * },\n * }\n * future: {v3_lineItemBilling: true}\n * });\n * export default shopify;\n * export const authenticate = shopify.authenticate;\n * ```\n */\n require: (\n options: RequireBillingOptions,\n ) => Promise;\n\n /**\n * Checks if the shop has an active payment for any plan defined in the `billing` config option.\n *\n * @returns A promise that resolves to an object containing the active purchases for the shop.\n *\n * @example\n * Check what billing plans a merchant is subscribed to.\n * Use billing.check if you want to determine which plans are in use. Unlike `require`, `check` does not\n * throw an error if no active billing plans are present. \n * ```ts\n * // /app/routes/**\\/*.ts\n * import { LoaderFunctionArgs } from \"@remix-run/node\";\n * import { authenticate, MONTHLY_PLAN } from \"../shopify.server\";\n *\n * export const loader = async ({ request }: LoaderFunctionArgs) => {\n * const { billing } = await authenticate.admin(request);\n * const { hasActivePayment, appSubscriptions } = await billing.check({\n * plans: [MONTHLY_PLAN],\n * isTest: false,\n * });\n * console.log(hasActivePayment)\n * console.log(appSubscriptions)\n * };\n * ```\n * ```ts\n * // shopify.server.ts\n * import { shopifyApp, BillingInterval } from \"@shopify/shopify-app-remix/server\";\n *\n * export const MONTHLY_PLAN = 'Monthly subscription';\n * export const ANNUAL_PLAN = 'Annual subscription';\n *\n * const shopify = shopifyApp({\n * // ...etc\n * billing: {\n * [MONTHLY_PLAN]: {\n * amount: 5,\n * currencyCode: 'USD',\n * interval: BillingInterval.Every30Days,\n * },\n * [ANNUAL_PLAN]: {\n * amount: 50,\n * currencyCode: 'USD',\n * interval: BillingInterval.Annual,\n * },\n * }\n * });\n * export default shopify;\n * export const authenticate = shopify.authenticate;\n * ```\n *\n */\n check: (\n options: CheckBillingOptions,\n ) => Promise;\n\n /**\n * Requests payment for the plan.\n *\n * @returns Redirects to the confirmation URL for the payment.\n *\n * @example\n * Using a custom return URL.\n * Change where the merchant is returned to after approving the purchase using the `returnUrl` option.\n * ```ts\n * // /app/routes/**\\/*.ts\n * import { LoaderFunctionArgs } from \"@remix-run/node\";\n * import { authenticate, MONTHLY_PLAN } from \"../shopify.server\";\n *\n * export const loader = async ({ request }: LoaderFunctionArgs) => {\n * const { billing } = await authenticate.admin(request);\n * await billing.require({\n * plans: [MONTHLY_PLAN],\n * onFailure: async () => billing.request({\n * plan: MONTHLY_PLAN,\n * isTest: true,\n * returnUrl: 'https://admin.shopify.com/store/my-store/apps/my-app/billing-page',\n * }),\n * });\n *\n * // App logic\n * };\n * ```\n * ```ts\n * // shopify.server.ts\n * import { shopifyApp, BillingInterval } from \"@shopify/shopify-app-remix/server\";\n *\n * export const MONTHLY_PLAN = 'Monthly subscription';\n * export const ANNUAL_PLAN = 'Annual subscription';\n *\n * const shopify = shopifyApp({\n * // ...etc\n * billing: {\n * [MONTHLY_PLAN]: {\n * amount: 5,\n * currencyCode: 'USD',\n * interval: BillingInterval.Every30Days,\n * },\n * [ANNUAL_PLAN]: {\n * amount: 50,\n * currencyCode: 'USD',\n * interval: BillingInterval.Annual,\n * },\n * }\n * });\n * export default shopify;\n * export const authenticate = shopify.authenticate;\n * ```\n */\n request: (options: RequestBillingOptions) => Promise;\n\n /**\n * Cancels an ongoing subscription, given its ID.\n *\n * @returns The cancelled subscription.\n *\n * @example\n * Cancelling a subscription.\n * Use the `billing.cancel` function to cancel an active subscription with the id returned from `billing.require`.\n * ```ts\n * // /app/routes/cancel-subscription.ts\n * import { LoaderFunctionArgs } from \"@remix-run/node\";\n * import { authenticate, MONTHLY_PLAN } from \"../shopify.server\";\n *\n * export const loader = async ({ request }: LoaderFunctionArgs) => {\n * const { billing } = await authenticate.admin(request);\n * const billingCheck = await billing.require({\n * plans: [MONTHLY_PLAN],\n * onFailure: async () => billing.request({ plan: MONTHLY_PLAN }),\n * });\n *\n * const subscription = billingCheck.appSubscriptions[0];\n * const cancelledSubscription = await billing.cancel({\n * subscriptionId: subscription.id,\n * isTest: true,\n * prorate: true,\n * });\n *\n * // App logic\n * };\n * ```\n * ```ts\n * // shopify.server.ts\n * import { shopifyApp, BillingInterval } from \"@shopify/shopify-app-remix/server\";\n *\n * export const MONTHLY_PLAN = 'Monthly subscription';\n * export const ANNUAL_PLAN = 'Annual subscription';\n *\n * const shopify = shopifyApp({\n * // ...etc\n * billing: {\n * [MONTHLY_PLAN]: {\n * amount: 5,\n * currencyCode: 'USD',\n * interval: BillingInterval.Every30Days,\n * },\n * [ANNUAL_PLAN]: {\n * amount: 50,\n * currencyCode: 'USD',\n * interval: BillingInterval.Annual,\n * },\n * }\n * });\n * export default shopify;\n * export const authenticate = shopify.authenticate;\n * ```\n */\n cancel: (options: CancelBillingOptions) => Promise;\n}" + "value": "export interface BillingContext {\n /**\n * Checks if the shop has an active payment for any plan defined in the `billing` config option.\n *\n * @returns A promise that resolves to an object containing the active purchases for the shop.\n *\n * @example\n * Requesting billing right away.\n * Call `billing.request` in the `onFailure` callback to immediately redirect to the Shopify page to request payment.\n * ```ts\n * // /app/routes/**\\/*.ts\n * import { LoaderFunctionArgs } from \"@remix-run/node\";\n * import { authenticate, MONTHLY_PLAN } from \"../shopify.server\";\n *\n * export const loader = async ({ request }: LoaderFunctionArgs) => {\n * const { billing } = await authenticate.admin(request);\n * await billing.require({\n * plans: [MONTHLY_PLAN],\n * isTest: true,\n * onFailure: async () => billing.request({ plan: MONTHLY_PLAN }),\n * });\n *\n * // App logic\n * };\n * ```\n * ```ts\n * // shopify.server.ts\n * import { shopifyApp, BillingInterval } from \"@shopify/shopify-app-remix/server\";\n *\n * export const MONTHLY_PLAN = 'Monthly subscription';\n * export const ANNUAL_PLAN = 'Annual subscription';\n *\n * const shopify = shopifyApp({\n * // ...etc\n * billing: {\n * [MONTHLY_PLAN]: {\n * amount: 5,\n * currencyCode: 'USD',\n * interval: BillingInterval.Every30Days,\n * },\n * [ANNUAL_PLAN]: {\n * amount: 50,\n * currencyCode: 'USD',\n * interval: BillingInterval.Annual,\n * },\n * }\n * });\n * export default shopify;\n * export const authenticate = shopify.authenticate;\n * ```\n *\n * @example\n * Redirect to a plan selection page.\n * When the app has multiple plans, create a page in your App that allows the merchant to select a plan. If a merchant does not have the required plan you can redirect them to page in your app to select one.\n * ```ts\n * // /app/routes/**\\/*.ts\n * import { LoaderFunctionArgs, redirect } from \"@remix-run/node\";\n * import { authenticate, MONTHLY_PLAN, ANNUAL_PLAN } from \"../shopify.server\";\n *\n * export const loader = async ({ request }: LoaderFunctionArgs) => {\n * const { billing } = await authenticate.admin(request);\n * const billingCheck = await billing.require({\n * plans: [MONTHLY_PLAN, ANNUAL_PLAN],\n * isTest: true,\n * onFailure: () => redirect('/select-plan'),\n * });\n *\n * const subscription = billingCheck.appSubscriptions[0];\n * console.log(`Shop is on ${subscription.name} (id ${subscription.id})`);\n *\n * // App logic\n * };\n * ```\n * ```ts\n * // shopify.server.ts\n * import { shopifyApp, BillingInterval } from \"@shopify/shopify-app-remix/server\";\n *\n * export const MONTHLY_PLAN = 'Monthly subscription';\n * export const ANNUAL_PLAN = 'Annual subscription';\n *\n * const shopify = shopifyApp({\n * // ...etc\n * billing: {\n * [MONTHLY_PLAN]: {\n * amount: 5,\n * currencyCode: 'USD',\n * interval: BillingInterval.Every30Days,\n * },\n * [ANNUAL_PLAN]: {\n * amount: 50,\n * currencyCode: 'USD',\n * interval: BillingInterval.Annual,\n * },\n * }\n * });\n * export default shopify;\n * export const authenticate = shopify.authenticate;\n * ```\n * @example\n * Requesting billing with line items\n * Call `billing.request` with the `v3_lineItemBilling` future flag enabled\n * ```ts\n * // /app/routes/**\\/*.ts\n * import { LoaderFunctionArgs } from \"@remix-run/node\";\n * import { authenticate, MONTHLY_PLAN } from \"../shopify.server\";\n *\n * export const loader = async ({ request }: LoaderFunctionArgs) => {\n * const { billing } = await authenticate.admin(request);\n * await billing.require({\n * plans: [MONTHLY_PLAN],\n * isTest: true,\n * onFailure: async () => billing.request({ plan: MONTHLY_PLAN }),\n * });\n *\n * // App logic\n * };\n * ```\n * ```ts\n * // shopify.server.ts\n * import { shopifyApp, BillingInterval } from \"@shopify/shopify-app-remix/server\";\n *\n * export const MONTHLY_PLAN = 'Monthly subscription';\n * export const ANNUAL_PLAN = 'Annual subscription';\n *\n * const shopify = shopifyApp({\n * // ...etc\n * billing: {\n * [MONTHLY_PLAN]: {\n * lineItems: [\n * {\n * amount: 5,\n * currencyCode: 'USD',\n * interval: BillingInterval.Every30Days,\n * },\n * {\n * amount: 1,\n * currencyCode: 'USD',\n * interval: BillingInterval.Usage.\n * terms: '1 dollar per 1000 emails',\n * },\n * ],\n * },\n * }\n * future: {v3_lineItemBilling: true}\n * });\n * export default shopify;\n * export const authenticate = shopify.authenticate;\n * ```\n */\n require: (\n options: RequireBillingOptions,\n ) => Promise;\n\n /**\n * Checks if the shop has an active payment for any plan defined in the `billing` config option.\n *\n * @returns A promise that resolves to an object containing the active purchases for the shop.\n *\n * @example\n * Check what billing plans a merchant is subscribed to.\n * Use billing.check if you want to determine which plans are in use. Unlike `require`, `check` does not\n * throw an error if no active billing plans are present. \n * ```ts\n * // /app/routes/**\\/*.ts\n * import { LoaderFunctionArgs } from \"@remix-run/node\";\n * import { authenticate, MONTHLY_PLAN } from \"../shopify.server\";\n *\n * export const loader = async ({ request }: LoaderFunctionArgs) => {\n * const { billing } = await authenticate.admin(request);\n * const { hasActivePayment, appSubscriptions } = await billing.check({\n * plans: [MONTHLY_PLAN],\n * isTest: false,\n * });\n * console.log(hasActivePayment)\n * console.log(appSubscriptions)\n * };\n * ```\n * ```ts\n * // shopify.server.ts\n * import { shopifyApp, BillingInterval } from \"@shopify/shopify-app-remix/server\";\n *\n * export const MONTHLY_PLAN = 'Monthly subscription';\n * export const ANNUAL_PLAN = 'Annual subscription';\n *\n * const shopify = shopifyApp({\n * // ...etc\n * billing: {\n * [MONTHLY_PLAN]: {\n * amount: 5,\n * currencyCode: 'USD',\n * interval: BillingInterval.Every30Days,\n * },\n * [ANNUAL_PLAN]: {\n * amount: 50,\n * currencyCode: 'USD',\n * interval: BillingInterval.Annual,\n * },\n * }\n * });\n * export default shopify;\n * export const authenticate = shopify.authenticate;\n * ```\n *\n */\n check: (\n options: CheckBillingOptions,\n ) => Promise;\n\n /**\n * Requests payment for the plan.\n *\n * @returns Redirects to the confirmation URL for the payment.\n *\n * @example\n * Using a custom return URL.\n * Change where the merchant is returned to after approving the purchase using the `returnUrl` option.\n * ```ts\n * // /app/routes/**\\/*.ts\n * import { LoaderFunctionArgs } from \"@remix-run/node\";\n * import { authenticate, MONTHLY_PLAN } from \"../shopify.server\";\n *\n * export const loader = async ({ request }: LoaderFunctionArgs) => {\n * const { billing } = await authenticate.admin(request);\n * await billing.require({\n * plans: [MONTHLY_PLAN],\n * onFailure: async () => billing.request({\n * plan: MONTHLY_PLAN,\n * isTest: true,\n * returnUrl: 'https://admin.shopify.com/store/my-store/apps/my-app/billing-page',\n * }),\n * });\n *\n * // App logic\n * };\n * ```\n * ```ts\n * // shopify.server.ts\n * import { shopifyApp, BillingInterval } from \"@shopify/shopify-app-remix/server\";\n *\n * export const MONTHLY_PLAN = 'Monthly subscription';\n * export const ANNUAL_PLAN = 'Annual subscription';\n *\n * const shopify = shopifyApp({\n * // ...etc\n * billing: {\n * [MONTHLY_PLAN]: {\n * amount: 5,\n * currencyCode: 'USD',\n * interval: BillingInterval.Every30Days,\n * },\n * [ANNUAL_PLAN]: {\n * amount: 50,\n * currencyCode: 'USD',\n * interval: BillingInterval.Annual,\n * },\n * }\n * });\n * export default shopify;\n * export const authenticate = shopify.authenticate;\n * ```\n */\n request: (options: RequestBillingOptions) => Promise;\n\n /**\n * Cancels an ongoing subscription, given its ID.\n *\n * @returns The cancelled subscription.\n *\n * @example\n * Cancelling a subscription.\n * Use the `billing.cancel` function to cancel an active subscription with the id returned from `billing.require`.\n * ```ts\n * // /app/routes/cancel-subscription.ts\n * import { LoaderFunctionArgs } from \"@remix-run/node\";\n * import { authenticate, MONTHLY_PLAN } from \"../shopify.server\";\n *\n * export const loader = async ({ request }: LoaderFunctionArgs) => {\n * const { billing } = await authenticate.admin(request);\n * const billingCheck = await billing.require({\n * plans: [MONTHLY_PLAN],\n * onFailure: async () => billing.request({ plan: MONTHLY_PLAN }),\n * });\n *\n * const subscription = billingCheck.appSubscriptions[0];\n * const cancelledSubscription = await billing.cancel({\n * subscriptionId: subscription.id,\n * isTest: true,\n * prorate: true,\n * });\n *\n * // App logic\n * };\n * ```\n * ```ts\n * // shopify.server.ts\n * import { shopifyApp, BillingInterval } from \"@shopify/shopify-app-remix/server\";\n *\n * export const MONTHLY_PLAN = 'Monthly subscription';\n * export const ANNUAL_PLAN = 'Annual subscription';\n *\n * const shopify = shopifyApp({\n * // ...etc\n * billing: {\n * [MONTHLY_PLAN]: {\n * amount: 5,\n * currencyCode: 'USD',\n * interval: BillingInterval.Every30Days,\n * },\n * [ANNUAL_PLAN]: {\n * amount: 50,\n * currencyCode: 'USD',\n * interval: BillingInterval.Annual,\n * },\n * }\n * });\n * export default shopify;\n * export const authenticate = shopify.authenticate;\n * ```\n */\n cancel: (options: CancelBillingOptions) => Promise;\n}" }, "RequireBillingOptions": { "filePath": "src/server/authenticate/admin/billing/types.ts", @@ -5624,12 +6158,12 @@ "description": "Get user-specific data using the `sessionToken` object.", "tabs": [ { - "code": "import { shopifyApp } from \"@shopify/shopify-app-remix/server\";\n\nconst shopify = shopifyApp({\n // ...etc\n useOnlineTokens: true,\n});\nexport default shopify;\nexport const authenticate = shopify.authenticate;", - "title": "shopify.server.ts" + "code": "import { LoaderFunctionArgs, json } from \"@remix-run/node\";\nimport { authenticate } from \"../shopify.server\";\nimport { getMyAppData } from \"~/db/model.server\";\n\nexport const loader = async ({ request }: LoaderFunctionArgs) => {\n const { sessionToken } = await authenticate.admin(\n request\n );\n return json(await getMyAppData({user: sessionToken.sub}));\n};", + "title": "/app/routes/**\\/*.ts" }, { - "code": "import { LoaderFunctionArgs, json } from \"@remix-run/node\";\nimport { authenticate } from \"../shopify.server\";\nimport { getMyAppData } from \"~/db/model.server\";\n\nexport const loader = async ({ request }: LoaderFunctionArgs) => {\n const { sessionToken } = await authenticate.public.checkout(\n request\n );\n return json(await getMyAppData({user: sessionToken.sub}));\n};", - "title": "/app/routes/**\\/*.ts" + "code": "import { shopifyApp } from \"@shopify/shopify-app-remix/server\";\n\nconst shopify = shopifyApp({\n // ...etc\n useOnlineTokens: true,\n});\nexport default shopify;\nexport const authenticate = shopify.authenticate;", + "title": "shopify.server.ts" } ] } @@ -5675,13 +6209,13 @@ "title": "Using offline sessions", "description": "Get your app's shop-specific data using an offline session.", "tabs": [ - { - "code": "import { shopifyApp } from \"@shopify/shopify-app-remix/server\";\n\nconst shopify = shopifyApp({\n // ...etc\n});\nexport default shopify;\nexport const authenticate = shopify.authenticate;", - "title": "shopify.server.ts" - }, { "code": "import { LoaderFunctionArgs, json } from \"@remix-run/node\";\nimport { authenticate } from \"../shopify.server\";\nimport { getMyAppData } from \"~/db/model.server\";\n\nexport const loader = async ({ request }: LoaderFunctionArgs) => {\n const { session } = await authenticate.admin(request);\n return json(await getMyAppData({shop: session.shop));\n};", "title": "/app/routes/**\\/*.ts" + }, + { + "code": "import { shopifyApp } from \"@shopify/shopify-app-remix/server\";\n\nconst shopify = shopifyApp({\n // ...etc\n});\nexport default shopify;\nexport const authenticate = shopify.authenticate;", + "title": "shopify.server.ts" } ] }, @@ -5689,13 +6223,13 @@ "title": "Using online sessions", "description": "Get your app's user-specific data using an online session.", "tabs": [ - { - "code": "import { shopifyApp } from \"@shopify/shopify-app-remix/server\";\n\nconst shopify = shopifyApp({\n // ...etc\n useOnlineTokens: true,\n});\nexport default shopify;\nexport const authenticate = shopify.authenticate;", - "title": "shopify.server.ts" - }, { "code": "import { LoaderFunctionArgs, json } from \"@remix-run/node\";\nimport { authenticate } from \"../shopify.server\";\nimport { getMyAppData } from \"~/db/model.server\";\n\nexport const loader = async ({ request }: LoaderFunctionArgs) => {\n const { session } = await authenticate.admin(request);\n return json(await getMyAppData({user: session.onlineAccessInfo!.id}));\n};", "title": "/app/routes/**\\/*.ts" + }, + { + "code": "import { shopifyApp } from \"@shopify/shopify-app-remix/server\";\n\nconst shopify = shopifyApp({\n // ...etc\n useOnlineTokens: true,\n});\nexport default shopify;\nexport const authenticate = shopify.authenticate;", + "title": "shopify.server.ts" } ] } @@ -5735,7 +6269,7 @@ ] } ], - "value": "export interface EmbeddedAdminContext<\n Config extends AppConfigArg,\n Resources extends ShopifyRestResources = ShopifyRestResources,\n> extends AdminContextInternal {\n /**\n * The decoded and validated session token for the request.\n *\n * Returned only if `isEmbeddedApp` is `true`.\n *\n * {@link https://shopify.dev/docs/apps/auth/oauth/session-tokens#payload}\n *\n * @example\n * Using the decoded session token.\n * Get user-specific data using the `sessionToken` object.\n * ```ts\n * // shopify.server.ts\n * import { shopifyApp } from \"@shopify/shopify-app-remix/server\";\n *\n * const shopify = shopifyApp({\n * // ...etc\n * useOnlineTokens: true,\n * });\n * export default shopify;\n * export const authenticate = shopify.authenticate;\n * ```\n * ```ts\n * // /app/routes/**\\/*.ts\n * import { LoaderFunctionArgs, json } from \"@remix-run/node\";\n * import { authenticate } from \"../shopify.server\";\n * import { getMyAppData } from \"~/db/model.server\";\n *\n * export const loader = async ({ request }: LoaderFunctionArgs) => {\n * const { sessionToken } = await authenticate.public.checkout(\n * request\n * );\n * return json(await getMyAppData({user: sessionToken.sub}));\n * };\n * ```\n */\n sessionToken: JwtPayload;\n\n /**\n * A function that redirects the user to a new page, ensuring that the appropriate parameters are set for embedded\n * apps.\n *\n * Returned only if `isEmbeddedApp` is `true`.\n *\n * @example\n * Redirecting to an app route.\n * Use the `redirect` helper to safely redirect between pages.\n * ```ts\n * // /app/routes/admin/my-route.ts\n * import { LoaderFunctionArgs, json } from \"@remix-run/node\";\n * import { authenticate } from \"../shopify.server\";\n *\n * export const loader = async ({ request }: LoaderFunctionArgs) => {\n * const { session, redirect } = await authenticate.admin(request);\n * return redirect(\"/\");\n * };\n * ```\n *\n * @example\n * Redirecting outside of Shopify admin.\n * Pass in a `target` option of `_top` or `_parent` to go to an external URL.\n * ```ts\n * // /app/routes/admin/my-route.ts\n * import { LoaderFunctionArgs, json } from \"@remix-run/node\";\n * import { authenticate } from \"../shopify.server\";\n *\n * export const loader = async ({ request }: LoaderFunctionArgs) => {\n * const { session, redirect } = await authenticate.admin(request);\n * return redirect(\"/\", { target: '_parent' });\n * };\n * ```\n */\n redirect: RedirectFunction;\n}" + "value": "export interface EmbeddedAdminContext<\n Config extends AppConfigArg,\n Resources extends ShopifyRestResources = ShopifyRestResources,\n> extends AdminContextInternal {\n /**\n * The decoded and validated session token for the request.\n *\n * Returned only if `isEmbeddedApp` is `true`.\n *\n * {@link https://shopify.dev/docs/apps/auth/oauth/session-tokens#payload}\n *\n * @example\n * Using the decoded session token.\n * Get user-specific data using the `sessionToken` object.\n * ```ts\n * // /app/routes/**\\/*.ts\n * import { LoaderFunctionArgs, json } from \"@remix-run/node\";\n * import { authenticate } from \"../shopify.server\";\n * import { getMyAppData } from \"~/db/model.server\";\n *\n * export const loader = async ({ request }: LoaderFunctionArgs) => {\n * const { sessionToken } = await authenticate.admin(\n * request\n * );\n * return json(await getMyAppData({user: sessionToken.sub}));\n * };\n * ```\n * ```ts\n * // shopify.server.ts\n * import { shopifyApp } from \"@shopify/shopify-app-remix/server\";\n *\n * const shopify = shopifyApp({\n * // ...etc\n * useOnlineTokens: true,\n * });\n * export default shopify;\n * export const authenticate = shopify.authenticate;\n * ```\n */\n sessionToken: JwtPayload;\n\n /**\n * A function that redirects the user to a new page, ensuring that the appropriate parameters are set for embedded\n * apps.\n *\n * Returned only if `isEmbeddedApp` is `true`.\n *\n * @example\n * Redirecting to an app route.\n * Use the `redirect` helper to safely redirect between pages.\n * ```ts\n * // /app/routes/admin/my-route.ts\n * import { LoaderFunctionArgs, json } from \"@remix-run/node\";\n * import { authenticate } from \"../shopify.server\";\n *\n * export const loader = async ({ request }: LoaderFunctionArgs) => {\n * const { session, redirect } = await authenticate.admin(request);\n * return redirect(\"/\");\n * };\n * ```\n *\n * @example\n * Redirecting outside of Shopify admin.\n * Pass in a `target` option of `_top` or `_parent` to go to an external URL.\n * ```ts\n * // /app/routes/admin/my-route.ts\n * import { LoaderFunctionArgs, json } from \"@remix-run/node\";\n * import { authenticate } from \"../shopify.server\";\n *\n * export const loader = async ({ request }: LoaderFunctionArgs) => {\n * const { session, redirect } = await authenticate.admin(request);\n * return redirect(\"/\", { target: '_parent' });\n * };\n * ```\n */\n redirect: RedirectFunction;\n}" }, "JwtPayload": { "filePath": "../../node_modules/@shopify/shopify-api/lib/session/types.d.ts", @@ -6169,49 +6703,6 @@ ], "value": "export interface DeleteRequestOptions extends GetRequestOptions {\n}" }, - "ApiVersion": { - "filePath": "../../node_modules/@shopify/shopify-api/lib/types.d.ts", - "syntaxKind": "EnumDeclaration", - "name": "ApiVersion", - "value": "export declare enum ApiVersion {\n October22 = \"2022-10\",\n January23 = \"2023-01\",\n April23 = \"2023-04\",\n July23 = \"2023-07\",\n October23 = \"2023-10\",\n January24 = \"2024-01\",\n Unstable = \"unstable\"\n}", - "members": [ - { - "filePath": "../../node_modules/@shopify/shopify-api/lib/types.d.ts", - "name": "October22", - "value": "2022-10" - }, - { - "filePath": "../../node_modules/@shopify/shopify-api/lib/types.d.ts", - "name": "January23", - "value": "2023-01" - }, - { - "filePath": "../../node_modules/@shopify/shopify-api/lib/types.d.ts", - "name": "April23", - "value": "2023-04" - }, - { - "filePath": "../../node_modules/@shopify/shopify-api/lib/types.d.ts", - "name": "July23", - "value": "2023-07" - }, - { - "filePath": "../../node_modules/@shopify/shopify-api/lib/types.d.ts", - "name": "October23", - "value": "2023-10" - }, - { - "filePath": "../../node_modules/@shopify/shopify-api/lib/types.d.ts", - "name": "January24", - "value": "2024-01" - }, - { - "filePath": "../../node_modules/@shopify/shopify-api/lib/types.d.ts", - "name": "Unstable", - "value": "unstable" - } - ] - }, "RestRequestReturn": { "filePath": "../../node_modules/@shopify/shopify-api/lib/clients/admin/types.d.ts", "name": "RestRequestReturn", @@ -6242,6 +6733,14 @@ ], "value": "export interface RestRequestReturn {\n body: T;\n headers: Headers;\n pageInfo?: PageInfo;\n}" }, + "Headers": { + "filePath": "../../node_modules/@shopify/shopify-api/runtime/http/types.d.ts", + "syntaxKind": "TypeAliasDeclaration", + "name": "Headers", + "value": "Record", + "description": "", + "members": [] + }, "PageInfo": { "filePath": "../../node_modules/@shopify/shopify-api/lib/clients/admin/types.d.ts", "name": "PageInfo", @@ -7492,108 +7991,6 @@ ], "value": "interface Readonly {\n readonly?: true;\n}" }, - "ApiClientFetch": { - "filePath": "../../node_modules/@shopify/shopify-api/node_modules/@shopify/graphql-client/dist/graphql-client.d.ts", - "name": "ApiClientFetch", - "description": "", - "params": [ - { - "name": "params", - "description": "", - "value": "ApiClientRequestParams", - "filePath": "../../node_modules/@shopify/shopify-api/node_modules/@shopify/graphql-client/dist/graphql-client.d.ts" - } - ], - "returns": { - "filePath": "../../node_modules/@shopify/shopify-api/node_modules/@shopify/graphql-client/dist/graphql-client.d.ts", - "description": "", - "name": "interface Promise {\n /**\n * Attaches callbacks for the resolution and/or rejection of the Promise.\n * @param onfulfilled The callback to execute when the Promise is resolved.\n * @param onrejected The callback to execute when the Promise is rejected.\n * @returns A Promise for the completion of which ever callback is executed.\n */\n then(onfulfilled?: ((value: T) => TResult1 | PromiseLike) | undefined | null, onrejected?: ((reason: any) => TResult2 | PromiseLike) | undefined | null): Promise;\n\n /**\n * Attaches a callback for only the rejection of the Promise.\n * @param onrejected The callback to execute when the Promise is rejected.\n * @returns A Promise for the completion of the callback.\n */\n catch(onrejected?: ((reason: any) => TResult | PromiseLike) | undefined | null): Promise;\n}, interface Promise {}, Promise: PromiseConstructor, interface Promise {\n readonly [Symbol.toStringTag]: string;\n}, interface Promise {\n /**\n * Attaches a callback that is invoked when the Promise is settled (fulfilled or rejected). The\n * resolved value cannot be modified from the callback.\n * @param onfinally The callback to execute when the Promise is settled (fulfilled or rejected).\n * @returns A Promise for the completion of the callback.\n */\n finally(onfinally?: (() => void) | undefined | null): Promise;\n}", - "value": "interface Promise {\n /**\n * Attaches callbacks for the resolution and/or rejection of the Promise.\n * @param onfulfilled The callback to execute when the Promise is resolved.\n * @param onrejected The callback to execute when the Promise is rejected.\n * @returns A Promise for the completion of which ever callback is executed.\n */\n then(onfulfilled?: ((value: T) => TResult1 | PromiseLike) | undefined | null, onrejected?: ((reason: any) => TResult2 | PromiseLike) | undefined | null): Promise;\n\n /**\n * Attaches a callback for only the rejection of the Promise.\n * @param onrejected The callback to execute when the Promise is rejected.\n * @returns A Promise for the completion of the callback.\n */\n catch(onrejected?: ((reason: any) => TResult | PromiseLike) | undefined | null): Promise;\n}, interface Promise {}, Promise: PromiseConstructor, interface Promise {\n readonly [Symbol.toStringTag]: string;\n}, interface Promise {\n /**\n * Attaches a callback that is invoked when the Promise is settled (fulfilled or rejected). The\n * resolved value cannot be modified from the callback.\n * @param onfinally The callback to execute when the Promise is settled (fulfilled or rejected).\n * @returns A Promise for the completion of the callback.\n */\n finally(onfinally?: (() => void) | undefined | null): Promise;\n}" - }, - "value": "type ApiClientFetch = (...params: ApiClientRequestParams) => Promise>>>;" - }, - "ApiClientRequestParams": { - "filePath": "../../node_modules/@shopify/shopify-api/node_modules/@shopify/graphql-client/dist/graphql-client.d.ts", - "syntaxKind": "TypeAliasDeclaration", - "name": "ApiClientRequestParams", - "value": "[\n operation: Operation,\n options?: ApiClientRequestOptions\n]", - "description": "", - "members": [ - { - "name": "[0]", - "value": "operation: Operation", - "filePath": "../../node_modules/@shopify/shopify-api/node_modules/@shopify/graphql-client/dist/graphql-client.d.ts" - }, - { - "name": "[1]", - "value": "options?: ApiClientRequestOptions", - "filePath": "../../node_modules/@shopify/shopify-api/node_modules/@shopify/graphql-client/dist/graphql-client.d.ts" - } - ] - }, - "ApiClientRequestOptions": { - "filePath": "../../node_modules/@shopify/shopify-api/node_modules/@shopify/graphql-client/dist/graphql-client.d.ts", - "syntaxKind": "TypeAliasDeclaration", - "name": "ApiClientRequestOptions", - "value": "{\n apiVersion?: string;\n headers?: Headers;\n retries?: number;\n} & (Operation extends keyof Operations ? OperationVariables : {\n variables?: Record;\n})", - "description": "" - }, - "Headers": { - "filePath": "../../node_modules/@shopify/shopify-api/node_modules/@shopify/graphql-client/dist/graphql-client.d.ts", - "syntaxKind": "TypeAliasDeclaration", - "name": "Headers", - "value": "Record", - "description": "", - "members": [] - }, - "OperationVariables": { - "filePath": "../../node_modules/@shopify/shopify-api/node_modules/@shopify/graphql-client/dist/graphql-client.d.ts", - "syntaxKind": "TypeAliasDeclaration", - "name": "OperationVariables", - "value": "Operations[Operation][\"variables\"] extends Record ? Record : {\n variables?: {\n [k in keyof Operations[Operation][\"variables\"]]: UnpackedInputMaybe;\n };\n}", - "description": "" - }, - "UnpackedInputMaybe": { - "filePath": "../../node_modules/@shopify/shopify-api/node_modules/@shopify/graphql-client/dist/graphql-client.d.ts", - "syntaxKind": "TypeAliasDeclaration", - "name": "UnpackedInputMaybe", - "value": "InputType extends InputMaybe ? InputMaybe> : UnpackedInput", - "description": "" - }, - "InputMaybe": { - "filePath": "../../node_modules/@shopify/shopify-api/node_modules/@shopify/graphql-client/dist/graphql-client.d.ts", - "syntaxKind": "TypeAliasDeclaration", - "name": "InputMaybe", - "value": "never", - "description": "" - }, - "UnpackedInput": { - "filePath": "../../node_modules/@shopify/shopify-api/node_modules/@shopify/graphql-client/dist/graphql-client.d.ts", - "syntaxKind": "TypeAliasDeclaration", - "name": "UnpackedInput", - "value": "\"input\" extends keyof InputType ? InputType[\"input\"] : InputType", - "description": "" - }, - "ApiClientRequest": { - "filePath": "../../node_modules/@shopify/shopify-api/node_modules/@shopify/graphql-client/dist/graphql-client.d.ts", - "name": "ApiClientRequest", - "description": "", - "params": [ - { - "name": "params", - "description": "", - "value": "ApiClientRequestParams", - "filePath": "../../node_modules/@shopify/shopify-api/node_modules/@shopify/graphql-client/dist/graphql-client.d.ts" - } - ], - "returns": { - "filePath": "../../node_modules/@shopify/shopify-api/node_modules/@shopify/graphql-client/dist/graphql-client.d.ts", - "description": "", - "name": "interface Promise {\n /**\n * Attaches callbacks for the resolution and/or rejection of the Promise.\n * @param onfulfilled The callback to execute when the Promise is resolved.\n * @param onrejected The callback to execute when the Promise is rejected.\n * @returns A Promise for the completion of which ever callback is executed.\n */\n then(onfulfilled?: ((value: T) => TResult1 | PromiseLike) | undefined | null, onrejected?: ((reason: any) => TResult2 | PromiseLike) | undefined | null): Promise;\n\n /**\n * Attaches a callback for only the rejection of the Promise.\n * @param onrejected The callback to execute when the Promise is rejected.\n * @returns A Promise for the completion of the callback.\n */\n catch(onrejected?: ((reason: any) => TResult | PromiseLike) | undefined | null): Promise;\n}, interface Promise {}, Promise: PromiseConstructor, interface Promise {\n readonly [Symbol.toStringTag]: string;\n}, interface Promise {\n /**\n * Attaches a callback that is invoked when the Promise is settled (fulfilled or rejected). The\n * resolved value cannot be modified from the callback.\n * @param onfinally The callback to execute when the Promise is settled (fulfilled or rejected).\n * @returns A Promise for the completion of the callback.\n */\n finally(onfinally?: (() => void) | undefined | null): Promise;\n}", - "value": "interface Promise {\n /**\n * Attaches callbacks for the resolution and/or rejection of the Promise.\n * @param onfulfilled The callback to execute when the Promise is resolved.\n * @param onrejected The callback to execute when the Promise is rejected.\n * @returns A Promise for the completion of which ever callback is executed.\n */\n then(onfulfilled?: ((value: T) => TResult1 | PromiseLike) | undefined | null, onrejected?: ((reason: any) => TResult2 | PromiseLike) | undefined | null): Promise;\n\n /**\n * Attaches a callback for only the rejection of the Promise.\n * @param onrejected The callback to execute when the Promise is rejected.\n * @returns A Promise for the completion of the callback.\n */\n catch(onrejected?: ((reason: any) => TResult | PromiseLike) | undefined | null): Promise;\n}, interface Promise {}, Promise: PromiseConstructor, interface Promise {\n readonly [Symbol.toStringTag]: string;\n}, interface Promise {\n /**\n * Attaches a callback that is invoked when the Promise is settled (fulfilled or rejected). The\n * resolved value cannot be modified from the callback.\n * @param onfinally The callback to execute when the Promise is settled (fulfilled or rejected).\n * @returns A Promise for the completion of the callback.\n */\n finally(onfinally?: (() => void) | undefined | null): Promise;\n}" - }, - "value": "type ApiClientRequest = (...params: ApiClientRequestParams) => Promise : TData>>;" - }, "GraphqlParams": { "filePath": "../../node_modules/@shopify/shopify-api/lib/clients/types.d.ts", "syntaxKind": "TypeAliasDeclaration", @@ -7656,111 +8053,24 @@ ], "value": "export interface GraphqlQueryOptions {\n /**\n * The variables to include in the operation.\n */\n variables?: ApiClientRequestOptions['variables'];\n /**\n * Additional headers to be sent with the request.\n */\n headers?: Record;\n /**\n * The maximum number of times to retry the request if it fails with a throttling or server error.\n */\n retries?: number;\n}" }, - "ClientResponse": { - "filePath": "../../node_modules/@shopify/shopify-api/node_modules/@shopify/graphql-client/dist/graphql-client.d.ts", - "name": "ClientResponse", + "StorefrontClient": { + "filePath": "../../node_modules/@shopify/shopify-api/lib/clients/storefront/client.d.ts", + "name": "StorefrontClient", "description": "", "members": [ { - "filePath": "../../node_modules/@shopify/shopify-api/node_modules/@shopify/graphql-client/dist/graphql-client.d.ts", - "syntaxKind": "PropertySignature", - "name": "errors", - "value": "ResponseErrors", - "description": "", - "isOptional": true + "filePath": "../../node_modules/@shopify/shopify-api/lib/clients/storefront/client.d.ts", + "syntaxKind": "PropertyDeclaration", + "name": "session", + "value": "Session", + "description": "" }, { - "filePath": "../../node_modules/@shopify/shopify-api/node_modules/@shopify/graphql-client/dist/graphql-client.d.ts", - "syntaxKind": "PropertySignature", - "name": "data", - "value": "TData", - "description": "", - "isOptional": true - }, - { - "filePath": "../../node_modules/@shopify/shopify-api/node_modules/@shopify/graphql-client/dist/graphql-client.d.ts", - "syntaxKind": "PropertySignature", - "name": "extensions", - "value": "GQLExtensions", - "description": "", - "isOptional": true - } - ], - "value": "interface ClientResponse extends FetchResponseBody {\n errors?: ResponseErrors;\n}" - }, - "ResponseErrors": { - "filePath": "../../node_modules/@shopify/shopify-api/node_modules/@shopify/graphql-client/dist/graphql-client.d.ts", - "name": "ResponseErrors", - "description": "", - "members": [ - { - "filePath": "../../node_modules/@shopify/shopify-api/node_modules/@shopify/graphql-client/dist/graphql-client.d.ts", - "syntaxKind": "PropertySignature", - "name": "networkStatusCode", - "value": "number", - "description": "", - "isOptional": true - }, - { - "filePath": "../../node_modules/@shopify/shopify-api/node_modules/@shopify/graphql-client/dist/graphql-client.d.ts", - "syntaxKind": "PropertySignature", - "name": "message", - "value": "string", - "description": "", - "isOptional": true - }, - { - "filePath": "../../node_modules/@shopify/shopify-api/node_modules/@shopify/graphql-client/dist/graphql-client.d.ts", - "syntaxKind": "PropertySignature", - "name": "graphQLErrors", - "value": "any[]", - "description": "", - "isOptional": true - }, - { - "filePath": "../../node_modules/@shopify/shopify-api/node_modules/@shopify/graphql-client/dist/graphql-client.d.ts", - "syntaxKind": "PropertySignature", - "name": "response", - "value": "Response", - "description": "", - "isOptional": true - } - ], - "value": "interface ResponseErrors {\n networkStatusCode?: number;\n message?: string;\n graphQLErrors?: any[];\n response?: Response;\n}" - }, - "GQLExtensions": { - "filePath": "../../node_modules/@shopify/shopify-api/node_modules/@shopify/graphql-client/dist/graphql-client.d.ts", - "syntaxKind": "TypeAliasDeclaration", - "name": "GQLExtensions", - "value": "Record", - "description": "", - "members": [] - }, - "ReturnData": { - "filePath": "../../node_modules/@shopify/shopify-api/node_modules/@shopify/graphql-client/dist/graphql-client.d.ts", - "syntaxKind": "TypeAliasDeclaration", - "name": "ReturnData", - "value": "Operation extends keyof Operations ? Operations[Operation][\"return\"] : any", - "description": "" - }, - "StorefrontClient": { - "filePath": "../../node_modules/@shopify/shopify-api/lib/clients/storefront/client.d.ts", - "name": "StorefrontClient", - "description": "", - "members": [ - { - "filePath": "../../node_modules/@shopify/shopify-api/lib/clients/storefront/client.d.ts", - "syntaxKind": "PropertyDeclaration", - "name": "session", - "value": "Session", - "description": "" - }, - { - "filePath": "../../node_modules/@shopify/shopify-api/lib/clients/storefront/client.d.ts", - "syntaxKind": "PropertyDeclaration", - "name": "client", - "value": "StorefrontApiClient", - "description": "" + "filePath": "../../node_modules/@shopify/shopify-api/lib/clients/storefront/client.d.ts", + "syntaxKind": "PropertyDeclaration", + "name": "client", + "value": "StorefrontApiClient", + "description": "" }, { "filePath": "../../node_modules/@shopify/shopify-api/lib/clients/storefront/client.d.ts", @@ -7790,45 +8100,15 @@ "filePath": "../../node_modules/@shopify/shopify-api/node_modules/@shopify/storefront-api-client/dist/storefront-api-client.d.ts", "syntaxKind": "TypeAliasDeclaration", "name": "StorefrontApiClient", - "value": "ApiClient", - "description": "", - "members": [ - { - "filePath": "../../node_modules/@shopify/shopify-api/node_modules/@shopify/storefront-api-client/dist/storefront-api-client.d.ts", - "syntaxKind": "PropertySignature", - "name": "config", - "value": "Readonly", - "description": "" - }, - { - "filePath": "../../node_modules/@shopify/shopify-api/node_modules/@shopify/storefront-api-client/dist/storefront-api-client.d.ts", - "syntaxKind": "PropertySignature", - "name": "getHeaders", - "value": "(headers?: Headers) => Headers", - "description": "" - }, - { - "filePath": "../../node_modules/@shopify/shopify-api/node_modules/@shopify/storefront-api-client/dist/storefront-api-client.d.ts", - "syntaxKind": "PropertySignature", - "name": "getApiUrl", - "value": "(apiVersion?: string) => string", - "description": "" - }, - { - "filePath": "../../node_modules/@shopify/shopify-api/node_modules/@shopify/storefront-api-client/dist/storefront-api-client.d.ts", - "syntaxKind": "PropertySignature", - "name": "fetch", - "value": "ApiClientFetch", - "description": "" - }, - { - "filePath": "../../node_modules/@shopify/shopify-api/node_modules/@shopify/storefront-api-client/dist/storefront-api-client.d.ts", - "syntaxKind": "PropertySignature", - "name": "request", - "value": "ApiClientRequest", - "description": "" - } - ] + "value": "ApiClient & {\n requestStream: ApiClientRequestStream;\n}", + "description": "" + }, + "StorefrontApiClientConfig": { + "filePath": "../../node_modules/@shopify/shopify-api/node_modules/@shopify/storefront-api-client/dist/storefront-api-client.d.ts", + "syntaxKind": "TypeAliasDeclaration", + "name": "StorefrontApiClientConfig", + "value": "ApiClientConfig & {\n clientName?: string;\n} & ({\n publicAccessToken?: never;\n privateAccessToken: string;\n} | {\n publicAccessToken: string;\n privateAccessToken?: never;\n})", + "description": "" }, "StorefrontOperations": { "filePath": "../../node_modules/@shopify/shopify-api/node_modules/@shopify/storefront-api-client/dist/storefront-api-client.d.ts", @@ -9504,7 +9784,7 @@ "tabs": [ { "title": "/app/routes/webhooks.tsx", - "code": "import { ActionFunction } from \"@remix-run/node\";\nimport { authenticate } from \"../shopify.server\";\n\nexport const action: ActionFunction = async ({ request }) => {\n const { apiVersion } = await authenticate.webhook(request);\n return new Response();\n};", + "code": "import { ActionFunctionArgs } from \"@remix-run/node\";\nimport { authenticate } from \"../shopify.server\";\n\nexport const action = async ({ request }: ActionFunctionArgs) => {\n const { apiVersion } = await authenticate.webhook(request);\n return new Response();\n};", "language": "typescript" } ] @@ -9522,7 +9802,7 @@ "tabs": [ { "title": "/app/routes/webhooks.tsx", - "code": "import { ActionFunction } from \"@remix-run/node\";\nimport { authenticate } from \"../shopify.server\";\n\nexport const action: ActionFunction = async ({ request }) => {\n const { shop } = await authenticate.webhook(request);\n return new Response();\n};", + "code": "import { ActionFunctionArgs } from \"@remix-run/node\";\nimport { authenticate } from \"../shopify.server\";\n\nexport const action = async ({ request }: ActionFunctionArgs) => {\n const { shop } = await authenticate.webhook(request);\n return new Response();\n};", "language": "typescript" } ] @@ -9540,7 +9820,7 @@ "tabs": [ { "title": "/app/routes/webhooks.tsx", - "code": "import { ActionFunction } from \"@remix-run/node\";\nimport { authenticate } from \"../shopify.server\";\n\nexport const action: ActionFunction = async ({ request }) => {\n const { topic } = await authenticate.webhook(request);\n\n switch (topic) {\n case \"APP_UNINSTALLED\":\n // Do something when the app is uninstalled.\n break;\n }\n\n return new Response();\n};", + "code": "import { ActionFunctionArgs } from \"@remix-run/node\";\nimport { authenticate } from \"../shopify.server\";\n\nexport const action = async ({ request }: ActionFunctionArgs) => {\n const { topic } = await authenticate.webhook(request);\n\n switch (topic) {\n case \"APP_UNINSTALLED\":\n // Do something when the app is uninstalled.\n break;\n }\n\n return new Response();\n};", "language": "typescript" } ] @@ -9558,7 +9838,7 @@ "tabs": [ { "title": "/app/routes/webhooks.tsx", - "code": "import { ActionFunction } from \"@remix-run/node\";\nimport { authenticate } from \"../shopify.server\";\n\nexport const action: ActionFunction = async ({ request }) => {\n const { webhookId } = await authenticate.webhook(request);\n return new Response();\n};", + "code": "import { ActionFunctionArgs } from \"@remix-run/node\";\nimport { authenticate } from \"../shopify.server\";\n\nexport const action = async ({ request }: ActionFunctionArgs) => {\n const { webhookId } = await authenticate.webhook(request);\n return new Response();\n};", "language": "typescript" } ] @@ -9576,7 +9856,7 @@ "tabs": [ { "title": "/app/routes/webhooks.tsx", - "code": "import { ActionFunction } from \"@remix-run/node\";\nimport { authenticate } from \"../shopify.server\";\n\nexport const action: ActionFunction = async ({ request }) => {\n const { payload } = await authenticate.webhook(request);\n return new Response();\n};", + "code": "import { ActionFunctionArgs } from \"@remix-run/node\";\nimport { authenticate } from \"../shopify.server\";\n\nexport const action = async ({ request }: ActionFunctionArgs) => {\n const { payload } = await authenticate.webhook(request);\n return new Response();\n};", "language": "typescript" } ] @@ -9594,7 +9874,7 @@ "tabs": [ { "title": "/app/routes/webhooks.tsx", - "code": "import { ActionFunction } from \"@remix-run/node\";\nimport { authenticate } from \"../shopify.server\";\n\nexport const action: ActionFunction = async ({ request }) => {\n const { subTopic } = await authenticate.webhook(request);\n return new Response();\n};", + "code": "import { ActionFunctionArgs } from \"@remix-run/node\";\nimport { authenticate } from \"../shopify.server\";\n\nexport const action = async ({ request }: ActionFunctionArgs) => {\n const { subTopic } = await authenticate.webhook(request);\n return new Response();\n};", "language": "typescript" } ] @@ -9634,7 +9914,7 @@ "description": "Getting the number of orders in a store using REST resources. Visit the [Admin REST API references](/docs/api/admin-rest) for examples on using each resource.", "tabs": [ { - "code": "import { LoaderFunctionArgs, json } from \"@remix-run/node\";\nimport { authenticate } from \"../shopify.server\";\n\nexport const loader = async ({ request }: LoaderFunctionArgs) => {\n const { admin, session } = await authenticate.admin(request);\n return json(admin.rest.resources.Order.count({ session }));\n};", + "code": "import { LoaderFunctionArgs, json } from \"@remix-run/node\";\nimport { authenticate } from \"../shopify.server\";\n\nexport const loader = async ({ request }: LoaderFunctionArgs) => {\n const {\n admin,\n session,\n } = await authenticate.admin(request);\n\n return json(\n admin.rest.resources.Order.count({ session }),\n );\n};", "title": "/app/routes/**\\/*.ts" }, { @@ -9648,7 +9928,7 @@ "description": "Use `admin.rest.get` to make custom requests to make a request to to the `customer/count` endpoint", "tabs": [ { - "code": "import { LoaderFunctionArgs, json } from \"@remix-run/node\";\nimport { authenticate } from \"../shopify.server\";\n\nexport const loader = async ({ request }: LoaderFunctionArgs) => {\n const { admin, session } = await authenticate.admin(request);\n const response = await admin.rest.get({ path: \"/customers/count.json\" });\n const customers = await response.json();\n return json({ customers });\n};", + "code": "import { LoaderFunctionArgs, json } from \"@remix-run/node\";\nimport { authenticate } from \"../shopify.server\";\n\nexport const loader = async ({ request }: LoaderFunctionArgs) => {\n const {\n admin,\n session,\n } = await authenticate.admin(request);\n\n const response = await admin.rest.get({\n path: \"/customers/count.json\",\n });\n const customers = await response.json();\n\n return json({ customers });\n};", "title": "/app/routes/**\\/*.ts" }, { @@ -9662,7 +9942,7 @@ "description": "Use `admin.rest.post` to make custom requests to make a request to to the `customers.json` endpoint to send a welcome email", "tabs": [ { - "code": "import { LoaderFunctionArgs, json } from \"@remix-run/node\";\nimport { authenticate } from \"../shopify.server\";\n\nexport const loader = async ({ request }: LoaderFunctionArgs) => {\n const { admin, session } = await authenticate.admin(request);\n const response = admin.rest.post({\n path: \"customers/7392136888625/send_invite.json\",\n body: {\n customer_invite: {\n to: \"new_test_email@shopify.com\",\n from: \"j.limited@example.com\",\n bcc: [\"j.limited@example.com\"],\n subject: \"Welcome to my new shop\",\n custom_message: \"My awesome new store\",\n },\n },\n});\n const customerInvite = await response.json();\n return json({ customerInvite });\n};", + "code": "import { LoaderFunctionArgs, json } from \"@remix-run/node\";\nimport { authenticate } from \"../shopify.server\";\n\nexport const loader = async ({ request }: LoaderFunctionArgs) => {\n const {\n admin,\n session,\n } = await authenticate.admin(request);\n\n const response = admin.rest.post({\n path: \"customers/7392136888625/send_invite.json\",\n body: {\n customer_invite: {\n to: \"new_test_email@shopify.com\",\n from: \"j.limited@example.com\",\n bcc: [\"j.limited@example.com\"],\n subject: \"Welcome to my new shop\",\n custom_message: \"My awesome new store\",\n },\n },\n });\n\n const customerInvite = await response.json();\n return json({ customerInvite });\n};", "title": "/app/routes/**\\/*.ts" }, { @@ -9685,11 +9965,25 @@ "description": "Use `admin.graphql` to make query / mutation requests.", "tabs": [ { - "code": "import { ActionFunctionArgs } from \"@remix-run/node\";\nimport { authenticate } from \"../shopify.server\";\n\nexport async function action({ request }: ActionFunctionArgs) {\n const { admin } = await authenticate.admin(request);\n\n const response = await admin.graphql(\n `#graphql\n mutation populateProduct($input: ProductInput!) {\n productCreate(input: $input) {\n product {\n id\n }\n }\n }`,\n { variables: { input: { title: \"Product Name\" } } }\n );\n\n const productData = await response.json();\n return json({ data: productData.data });\n}", - "title": "Example" + "code": "import { ActionFunctionArgs } from \"@remix-run/node\";\nimport { authenticate } from \"../shopify.server\";\n\nexport const action = async ({ request }: ActionFunctionArgs) => {\n const { admin } = await authenticate.admin(request);\n\n const response = await admin.graphql(\n `#graphql\n mutation populateProduct($input: ProductInput!) {\n productCreate(input: $input) {\n product {\n id\n }\n }\n }`,\n {\n variables: {\n input: { title: \"Product Name\" },\n },\n },\n );\n\n const productData = await response.json();\n return json({\n productId: productData.data?.productCreate?.product?.id,\n });\n}", + "title": "/app/routes/**\\/*.ts" + }, + { + "code": "import { shopifyApp } from \"@shopify/shopify-app-remix/server\";\n\nconst shopify = shopifyApp({\n // ...\n});\nexport default shopify;\nexport const authenticate = shopify.authenticate;", + "title": "/app/shopify.server.ts" + } + ] + }, + { + "title": "Handling GraphQL errors", + "description": "Catch `GraphqlQueryError` errors to see error messages from the API.", + "tabs": [ + { + "code": "import { ActionFunctionArgs } from \"@remix-run/node\";\nimport { authenticate } from \"../shopify.server\";\n\nexport const action = async ({ request }: ActionFunctionArgs) => {\n const { admin } = await authenticate.admin(request);\n\n try {\n const response = await admin.graphql(\n `#graphql\n query incorrectQuery {\n products(first: 10) {\n nodes {\n not_a_field\n }\n }\n }`,\n );\n\n return json({ data: await response.json() });\n } catch (error) {\n if (error instanceof GraphqlQueryError) {\n // error.body.errors:\n // { graphQLErrors: [\n // { message: \"Field 'not_a_field' doesn't exist on type 'Product'\" }\n // ] }\n return json({ errors: error.body?.errors }, { status: 500 });\n }\n return json({ message: \"An error occurred\" }, { status: 500 });\n }\n}", + "title": "/app/routes/**\\/*.ts" }, { - "code": "import { shopifyApp } from \"@shopify/shopify-app-remix/server\";\n\nconst shopify = shopifyApp({\n restResources,\n // ...etc\n});\nexport default shopify;\nexport const unauthenticated = shopify.unauthenticated;", + "code": "import { shopifyApp } from \"@shopify/shopify-app-remix/server\";\n\nconst shopify = shopifyApp({\n // ...\n});\nexport default shopify;\nexport const authenticate = shopify.authenticate;", "title": "/app/shopify.server.ts" } ] @@ -9697,7 +9991,7 @@ ] } ], - "value": "export interface AdminApiContext<\n Resources extends ShopifyRestResources = ShopifyRestResources,\n> {\n /**\n * Methods for interacting with the Shopify Admin REST API\n *\n * There are methods for interacting with individual REST resources. You can also make `GET`, `POST`, `PUT` and `DELETE` requests should the REST resources not meet your needs.\n *\n * {@link https://shopify.dev/docs/api/admin-rest}\n *\n * @example\n * Using REST resources.\n * Getting the number of orders in a store using REST resources. Visit the [Admin REST API references](/docs/api/admin-rest) for examples on using each resource. \n *\n * ```ts\n * // /app/routes/**\\/*.ts\n * import { LoaderFunctionArgs, json } from \"@remix-run/node\";\n * import { authenticate } from \"../shopify.server\";\n *\n * export const loader = async ({ request }: LoaderFunctionArgs) => {\n * const { admin, session } = await authenticate.admin(request);\n * return json(admin.rest.resources.Order.count({ session }));\n * };\n * ```\n *\n *\n * ```ts\n * // /app/shopify.server.ts\n * import { shopifyApp } from \"@shopify/shopify-app-remix/server\";\n * import { restResources } from \"@shopify/shopify-api/rest/admin/2023-07\";\n *\n * const shopify = shopifyApp({\n * restResources,\n * // ...etc\n * });\n * export default shopify;\n * export const authenticate = shopify.authenticate;\n * ```\n *\n *\n * @example\n * Performing a GET request to the REST API.\n * Use `admin.rest.get` to make custom requests to make a request to to the `customer/count` endpoint\n *\n * ```ts\n * // /app/routes/**\\/*.ts\n * import { LoaderFunctionArgs, json } from \"@remix-run/node\";\n * import { authenticate } from \"../shopify.server\";\n *\n * export const loader = async ({ request }: LoaderFunctionArgs) => {\n * const { admin, session } = await authenticate.admin(request);\n * const response = await admin.rest.get({ path: \"/customers/count.json\" });\n * const customers = await response.json();\n * return json({ customers });\n * };\n * ```\n *\n * ```ts\n * // /app/shopify.server.ts\n * import { shopifyApp } from \"@shopify/shopify-app-remix/server\";\n * import { restResources } from \"@shopify/shopify-api/rest/admin/2023-04\";\n *\n * const shopify = shopifyApp({\n * restResources,\n * // ...etc\n * });\n * export default shopify;\n * export const authenticate = shopify.authenticate;\n * ```\n * @example\n * Performing a POST request to the REST API.\n * Use `admin.rest.post` to make custom requests to make a request to to the `customers.json` endpoint to send a welcome email\n * ```ts\n * // /app/routes/**\\/*.ts\n * import { LoaderFunctionArgs, json } from \"@remix-run/node\";\n * import { authenticate } from \"../shopify.server\";\n *\n * export const loader = async ({ request }: LoaderFunctionArgs) => {\n * const { admin, session } = await authenticate.admin(request);\n * const response = admin.rest.post({\n * path: \"customers/7392136888625/send_invite.json\",\n * body: {\n * customer_invite: {\n * to: \"new_test_email@shopify.com\",\n * from: \"j.limited@example.com\",\n * bcc: [\"j.limited@example.com\"],\n * subject: \"Welcome to my new shop\",\n * custom_message: \"My awesome new store\",\n * },\n * },\n * });\n * const customerInvite = await response.json();\n * return json({ customerInvite });\n * };\n * ```\n *\n * ```ts\n * // /app/shopify.server.ts\n * import { shopifyApp } from \"@shopify/shopify-app-remix/server\";\n * import { restResources } from \"@shopify/shopify-api/rest/admin/2023-04\";\n *\n * const shopify = shopifyApp({\n * restResources,\n * // ...etc\n * });\n * export default shopify;\n * export const authenticate = shopify.authenticate;\n * ```\n */\n rest: RestClientWithResources;\n\n /**\n * Methods for interacting with the Shopify Admin GraphQL API\n *\n * {@link https://shopify.dev/docs/api/admin-graphql}\n * {@link https://github.com/Shopify/shopify-api-js/blob/main/packages/shopify-api/docs/reference/clients/Graphql.md}\n *\n * @example\n * Querying the GraphQL API.\n * Use `admin.graphql` to make query / mutation requests.\n * ```ts\n * import { ActionFunctionArgs } from \"@remix-run/node\";\n * import { authenticate } from \"../shopify.server\";\n *\n * export async function action({ request }: ActionFunctionArgs) {\n * const { admin } = await authenticate.admin(request);\n *\n * const response = await admin.graphql(\n * `#graphql\n * mutation populateProduct($input: ProductInput!) {\n * productCreate(input: $input) {\n * product {\n * id\n * }\n * }\n * }`,\n * { variables: { input: { title: \"Product Name\" } } }\n * );\n *\n * const productData = await response.json();\n * return json({ data: productData.data });\n * }\n * ```\n *\n * ```ts\n * // /app/shopify.server.ts\n * import { shopifyApp } from \"@shopify/shopify-app-remix/server\";\n *\n * const shopify = shopifyApp({\n * restResources,\n * // ...etc\n * });\n * export default shopify;\n * export const unauthenticated = shopify.unauthenticated;\n * ```\n */\n graphql: GraphQLClient;\n}" + "value": "export interface AdminApiContext<\n Resources extends ShopifyRestResources = ShopifyRestResources,\n> {\n /**\n * Methods for interacting with the Shopify Admin REST API\n *\n * There are methods for interacting with individual REST resources. You can also make `GET`, `POST`, `PUT` and `DELETE` requests should the REST resources not meet your needs.\n *\n * {@link https://shopify.dev/docs/api/admin-rest}\n *\n * @example\n * Using REST resources.\n * Getting the number of orders in a store using REST resources. Visit the [Admin REST API references](/docs/api/admin-rest) for examples on using each resource. \n *\n * ```ts\n * // /app/routes/**\\/*.ts\n * import { LoaderFunctionArgs, json } from \"@remix-run/node\";\n * import { authenticate } from \"../shopify.server\";\n *\n * export const loader = async ({ request }: LoaderFunctionArgs) => {\n * const {\n * admin,\n * session,\n * } = await authenticate.admin(request);\n *\n * return json(\n * admin.rest.resources.Order.count({ session }),\n * );\n * };\n * ```\n *\n * ```ts\n * // /app/shopify.server.ts\n * import { shopifyApp } from \"@shopify/shopify-app-remix/server\";\n * import { restResources } from \"@shopify/shopify-api/rest/admin/2023-07\";\n *\n * const shopify = shopifyApp({\n * restResources,\n * // ...etc\n * });\n * export default shopify;\n * export const authenticate = shopify.authenticate;\n * ```\n *\n * @example\n * Performing a GET request to the REST API.\n * Use `admin.rest.get` to make custom requests to make a request to to the `customer/count` endpoint\n *\n * ```ts\n * // /app/routes/**\\/*.ts\n * import { LoaderFunctionArgs, json } from \"@remix-run/node\";\n * import { authenticate } from \"../shopify.server\";\n *\n * export const loader = async ({ request }: LoaderFunctionArgs) => {\n * const {\n * admin,\n * session,\n * } = await authenticate.admin(request);\n *\n * const response = await admin.rest.get({\n * path: \"/customers/count.json\",\n * });\n * const customers = await response.json();\n *\n * return json({ customers });\n * };\n * ```\n *\n * ```ts\n * // /app/shopify.server.ts\n * import { shopifyApp } from \"@shopify/shopify-app-remix/server\";\n * import { restResources } from \"@shopify/shopify-api/rest/admin/2023-04\";\n *\n * const shopify = shopifyApp({\n * restResources,\n * // ...etc\n * });\n * export default shopify;\n * export const authenticate = shopify.authenticate;\n * ```\n *\n * @example\n * Performing a POST request to the REST API.\n * Use `admin.rest.post` to make custom requests to make a request to to the `customers.json` endpoint to send a welcome email\n * ```ts\n * // /app/routes/**\\/*.ts\n * import { LoaderFunctionArgs, json } from \"@remix-run/node\";\n * import { authenticate } from \"../shopify.server\";\n *\n * export const loader = async ({ request }: LoaderFunctionArgs) => {\n * const {\n * admin,\n * session,\n * } = await authenticate.admin(request);\n *\n * const response = admin.rest.post({\n * path: \"customers/7392136888625/send_invite.json\",\n * body: {\n * customer_invite: {\n * to: \"new_test_email@shopify.com\",\n * from: \"j.limited@example.com\",\n * bcc: [\"j.limited@example.com\"],\n * subject: \"Welcome to my new shop\",\n * custom_message: \"My awesome new store\",\n * },\n * },\n * });\n *\n * const customerInvite = await response.json();\n * return json({ customerInvite });\n * };\n * ```\n *\n * ```ts\n * // /app/shopify.server.ts\n * import { shopifyApp } from \"@shopify/shopify-app-remix/server\";\n * import { restResources } from \"@shopify/shopify-api/rest/admin/2023-04\";\n *\n * const shopify = shopifyApp({\n * restResources,\n * // ...etc\n * });\n * export default shopify;\n * export const authenticate = shopify.authenticate;\n * ```\n */\n rest: RestClientWithResources;\n\n /**\n * Methods for interacting with the Shopify Admin GraphQL API\n *\n * {@link https://shopify.dev/docs/api/admin-graphql}\n * {@link https://github.com/Shopify/shopify-api-js/blob/main/packages/shopify-api/docs/reference/clients/Graphql.md}\n *\n * @example\n * Querying the GraphQL API.\n * Use `admin.graphql` to make query / mutation requests.\n * ```ts\n * // /app/routes/**\\/*.ts\n * import { ActionFunctionArgs } from \"@remix-run/node\";\n * import { authenticate } from \"../shopify.server\";\n *\n * export const action = async ({ request }: ActionFunctionArgs) => {\n * const { admin } = await authenticate.admin(request);\n *\n * const response = await admin.graphql(\n * `#graphql\n * mutation populateProduct($input: ProductInput!) {\n * productCreate(input: $input) {\n * product {\n * id\n * }\n * }\n * }`,\n * {\n * variables: {\n * input: { title: \"Product Name\" },\n * },\n * },\n * );\n *\n * const productData = await response.json();\n * return json({\n * productId: productData.data?.productCreate?.product?.id,\n * });\n * }\n * ```\n *\n * ```ts\n * // /app/shopify.server.ts\n * import { shopifyApp } from \"@shopify/shopify-app-remix/server\";\n *\n * const shopify = shopifyApp({\n * // ...\n * });\n * export default shopify;\n * export const authenticate = shopify.authenticate;\n * ```\n *\n * @example\n * Handling GraphQL errors.\n * Catch `GraphqlQueryError` errors to see error messages from the API.\n * ```ts\n * // /app/routes/**\\/*.ts\n * import { ActionFunctionArgs } from \"@remix-run/node\";\n * import { authenticate } from \"../shopify.server\";\n *\n * export const action = async ({ request }: ActionFunctionArgs) => {\n * const { admin } = await authenticate.admin(request);\n *\n * try {\n * const response = await admin.graphql(\n * `#graphql\n * query incorrectQuery {\n * products(first: 10) {\n * nodes {\n * not_a_field\n * }\n * }\n * }`,\n * );\n *\n * return json({ data: await response.json() });\n * } catch (error) {\n * if (error instanceof GraphqlQueryError) {\n * // error.body.errors:\n * // { graphQLErrors: [\n * // { message: \"Field 'not_a_field' doesn't exist on type 'Product'\" }\n * // ] }\n * return json({ errors: error.body?.errors }, { status: 500 });\n * }\n * return json({ message: \"An error occurred\" }, { status: 500 });\n * }\n * }\n * ```\n *\n * ```ts\n * // /app/shopify.server.ts\n * import { shopifyApp } from \"@shopify/shopify-app-remix/server\";\n *\n * const shopify = shopifyApp({\n * // ...\n * });\n * export default shopify;\n * export const authenticate = shopify.authenticate;\n * ```\n */\n graphql: GraphQLClient;\n}" }, "RestClientWithResources": { "filePath": "src/server/clients/admin/rest.ts", @@ -10089,6 +10383,116 @@ "value": "GetRequestParams & {\n data: Record | string;\n}", "description": "" }, + "GraphQLClient": { + "filePath": "src/server/clients/types.ts", + "name": "GraphQLClient", + "description": "", + "params": [ + { + "name": "query", + "description": "", + "value": "Operation extends keyof Operations", + "filePath": "src/server/clients/types.ts" + }, + { + "name": "options", + "description": "", + "value": "GraphQLQueryOptions", + "isOptional": true, + "filePath": "src/server/clients/types.ts" + } + ], + "returns": { + "filePath": "src/server/clients/types.ts", + "description": "", + "name": "interface Promise {\n /**\n * Attaches callbacks for the resolution and/or rejection of the Promise.\n * @param onfulfilled The callback to execute when the Promise is resolved.\n * @param onrejected The callback to execute when the Promise is rejected.\n * @returns A Promise for the completion of which ever callback is executed.\n */\n then(onfulfilled?: ((value: T) => TResult1 | PromiseLike) | undefined | null, onrejected?: ((reason: any) => TResult2 | PromiseLike) | undefined | null): Promise;\n\n /**\n * Attaches a callback for only the rejection of the Promise.\n * @param onrejected The callback to execute when the Promise is rejected.\n * @returns A Promise for the completion of the callback.\n */\n catch(onrejected?: ((reason: any) => TResult | PromiseLike) | undefined | null): Promise;\n}, interface Promise {}, Promise: PromiseConstructor, interface Promise {\n readonly [Symbol.toStringTag]: string;\n}, interface Promise {\n /**\n * Attaches a callback that is invoked when the Promise is settled (fulfilled or rejected). The\n * resolved value cannot be modified from the callback.\n * @param onfinally The callback to execute when the Promise is settled (fulfilled or rejected).\n * @returns A Promise for the completion of the callback.\n */\n finally(onfinally?: (() => void) | undefined | null): Promise;\n}", + "value": "interface Promise {\n /**\n * Attaches callbacks for the resolution and/or rejection of the Promise.\n * @param onfulfilled The callback to execute when the Promise is resolved.\n * @param onrejected The callback to execute when the Promise is rejected.\n * @returns A Promise for the completion of which ever callback is executed.\n */\n then(onfulfilled?: ((value: T) => TResult1 | PromiseLike) | undefined | null, onrejected?: ((reason: any) => TResult2 | PromiseLike) | undefined | null): Promise;\n\n /**\n * Attaches a callback for only the rejection of the Promise.\n * @param onrejected The callback to execute when the Promise is rejected.\n * @returns A Promise for the completion of the callback.\n */\n catch(onrejected?: ((reason: any) => TResult | PromiseLike) | undefined | null): Promise;\n}, interface Promise {}, Promise: PromiseConstructor, interface Promise {\n readonly [Symbol.toStringTag]: string;\n}, interface Promise {\n /**\n * Attaches a callback that is invoked when the Promise is settled (fulfilled or rejected). The\n * resolved value cannot be modified from the callback.\n * @param onfinally The callback to execute when the Promise is settled (fulfilled or rejected).\n * @returns A Promise for the completion of the callback.\n */\n finally(onfinally?: (() => void) | undefined | null): Promise;\n}" + }, + "value": "export type GraphQLClient = <\n Operation extends keyof Operations,\n>(\n query: Operation,\n options?: GraphQLQueryOptions,\n) => Promise>;" + }, + "GraphQLQueryOptions": { + "filePath": "src/server/clients/types.ts", + "name": "GraphQLQueryOptions", + "description": "", + "members": [ + { + "filePath": "src/server/clients/types.ts", + "syntaxKind": "PropertySignature", + "name": "variables", + "value": "ApiClientRequestOptions[\"variables\"]", + "description": "The variables to pass to the operation.", + "isOptional": true + }, + { + "filePath": "src/server/clients/types.ts", + "syntaxKind": "PropertySignature", + "name": "apiVersion", + "value": "ApiVersion", + "description": "The version of the API to use for the request.", + "isOptional": true + }, + { + "filePath": "src/server/clients/types.ts", + "syntaxKind": "PropertySignature", + "name": "headers", + "value": "Record", + "description": "Additional headers to include in the request.", + "isOptional": true + }, + { + "filePath": "src/server/clients/types.ts", + "syntaxKind": "PropertySignature", + "name": "tries", + "value": "number", + "description": "The total number of times to try the request if it fails.", + "isOptional": true + } + ], + "value": "export interface GraphQLQueryOptions<\n Operation extends keyof Operations,\n Operations extends AllOperations,\n> {\n /**\n * The variables to pass to the operation.\n */\n variables?: ApiClientRequestOptions['variables'];\n /**\n * The version of the API to use for the request.\n */\n apiVersion?: ApiVersion;\n /**\n * Additional headers to include in the request.\n */\n headers?: Record;\n /**\n * The total number of times to try the request if it fails.\n */\n tries?: number;\n}" + }, + "ApiVersion": { + "filePath": "../../node_modules/@shopify/shopify-api/lib/types.d.ts", + "syntaxKind": "EnumDeclaration", + "name": "ApiVersion", + "value": "export declare enum ApiVersion {\n October22 = \"2022-10\",\n January23 = \"2023-01\",\n April23 = \"2023-04\",\n July23 = \"2023-07\",\n October23 = \"2023-10\",\n January24 = \"2024-01\",\n Unstable = \"unstable\"\n}", + "members": [ + { + "filePath": "../../node_modules/@shopify/shopify-api/lib/types.d.ts", + "name": "October22", + "value": "2022-10" + }, + { + "filePath": "../../node_modules/@shopify/shopify-api/lib/types.d.ts", + "name": "January23", + "value": "2023-01" + }, + { + "filePath": "../../node_modules/@shopify/shopify-api/lib/types.d.ts", + "name": "April23", + "value": "2023-04" + }, + { + "filePath": "../../node_modules/@shopify/shopify-api/lib/types.d.ts", + "name": "July23", + "value": "2023-07" + }, + { + "filePath": "../../node_modules/@shopify/shopify-api/lib/types.d.ts", + "name": "October23", + "value": "2023-10" + }, + { + "filePath": "../../node_modules/@shopify/shopify-api/lib/types.d.ts", + "name": "January24", + "value": "2024-01" + }, + { + "filePath": "../../node_modules/@shopify/shopify-api/lib/types.d.ts", + "name": "Unstable", + "value": "unstable" + } + ] + }, "AdminOperations": { "filePath": "../../node_modules/@shopify/shopify-api/node_modules/@shopify/admin-api-client/dist/ts/graphql/types.d.ts", "syntaxKind": "TypeAliasDeclaration", @@ -10163,7 +10567,7 @@ "tabs": [ { "title": "/app/routes/**\\/*.ts", - "code": "import { LoaderFunctionArgs, json } from \"@remix-run/node\";\nimport { authenticate } from \"../shopify.server\";\n\nexport const loader = async ({ request }: LoaderFunctionArgs) => {\n const { admin, session } = await authenticate.admin(request);\n return json(admin.rest.resources.Order.count({ session }));\n};", + "code": "import { LoaderFunctionArgs, json } from \"@remix-run/node\";\nimport { authenticate } from \"../shopify.server\";\n\nexport const loader = async ({ request }: LoaderFunctionArgs) => {\n const {\n admin,\n session,\n } = await authenticate.admin(request);\n\n return json(\n admin.rest.resources.Order.count({ session }),\n );\n};", "language": "typescript" }, { @@ -10181,7 +10585,7 @@ "tabs": [ { "title": "/app/routes/**\\/*.ts", - "code": "import { LoaderFunctionArgs, json } from \"@remix-run/node\";\nimport { authenticate } from \"../shopify.server\";\n\nexport const loader = async ({ request }: LoaderFunctionArgs) => {\n const { admin, session } = await authenticate.admin(request);\n const response = await admin.rest.get({ path: \"/customers/count.json\" });\n const customers = await response.json();\n return json({ customers });\n};", + "code": "import { LoaderFunctionArgs, json } from \"@remix-run/node\";\nimport { authenticate } from \"../shopify.server\";\n\nexport const loader = async ({ request }: LoaderFunctionArgs) => {\n const {\n admin,\n session,\n } = await authenticate.admin(request);\n\n const response = await admin.rest.get({\n path: \"/customers/count.json\",\n });\n const customers = await response.json();\n\n return json({ customers });\n};", "language": "typescript" }, { @@ -10199,7 +10603,7 @@ "tabs": [ { "title": "/app/routes/**\\/*.ts", - "code": "import { LoaderFunctionArgs, json } from \"@remix-run/node\";\nimport { authenticate } from \"../shopify.server\";\n\nexport const loader = async ({ request }: LoaderFunctionArgs) => {\n const { admin, session } = await authenticate.admin(request);\n const response = admin.rest.post({\n path: \"customers/7392136888625/send_invite.json\",\n body: {\n customer_invite: {\n to: \"new_test_email@shopify.com\",\n from: \"j.limited@example.com\",\n bcc: [\"j.limited@example.com\"],\n subject: \"Welcome to my new shop\",\n custom_message: \"My awesome new store\",\n },\n },\n});\n const customerInvite = await response.json();\n return json({ customerInvite });\n};", + "code": "import { LoaderFunctionArgs, json } from \"@remix-run/node\";\nimport { authenticate } from \"../shopify.server\";\n\nexport const loader = async ({ request }: LoaderFunctionArgs) => {\n const {\n admin,\n session,\n } = await authenticate.admin(request);\n\n const response = admin.rest.post({\n path: \"customers/7392136888625/send_invite.json\",\n body: {\n customer_invite: {\n to: \"new_test_email@shopify.com\",\n from: \"j.limited@example.com\",\n bcc: [\"j.limited@example.com\"],\n subject: \"Welcome to my new shop\",\n custom_message: \"My awesome new store\",\n },\n },\n });\n\n const customerInvite = await response.json();\n return json({ customerInvite });\n};", "language": "typescript" }, { @@ -10221,13 +10625,31 @@ "title": "Querying the GraphQL API", "tabs": [ { - "title": "Example", - "code": "import { ActionFunctionArgs } from \"@remix-run/node\";\nimport { authenticate } from \"../shopify.server\";\n\nexport async function action({ request }: ActionFunctionArgs) {\n const { admin } = await authenticate.admin(request);\n\n const response = await admin.graphql(\n `#graphql\n mutation populateProduct($input: ProductInput!) {\n productCreate(input: $input) {\n product {\n id\n }\n }\n }`,\n { variables: { input: { title: \"Product Name\" } } }\n );\n\n const productData = await response.json();\n return json({ data: productData.data });\n}", + "title": "/app/routes/**\\/*.ts", + "code": "import { ActionFunctionArgs } from \"@remix-run/node\";\nimport { authenticate } from \"../shopify.server\";\n\nexport const action = async ({ request }: ActionFunctionArgs) => {\n const { admin } = await authenticate.admin(request);\n\n const response = await admin.graphql(\n `#graphql\n mutation populateProduct($input: ProductInput!) {\n productCreate(input: $input) {\n product {\n id\n }\n }\n }`,\n {\n variables: {\n input: { title: \"Product Name\" },\n },\n },\n );\n\n const productData = await response.json();\n return json({\n productId: productData.data?.productCreate?.product?.id,\n });\n}", + "language": "typescript" + }, + { + "title": "/app/shopify.server.ts", + "code": "import { shopifyApp } from \"@shopify/shopify-app-remix/server\";\n\nconst shopify = shopifyApp({\n // ...\n});\nexport default shopify;\nexport const authenticate = shopify.authenticate;", + "language": "typescript" + } + ] + } + }, + { + "description": "Catch `GraphqlQueryError` errors to see error messages from the API.", + "codeblock": { + "title": "Handling GraphQL errors", + "tabs": [ + { + "title": "/app/routes/**\\/*.ts", + "code": "import { ActionFunctionArgs } from \"@remix-run/node\";\nimport { authenticate } from \"../shopify.server\";\n\nexport const action = async ({ request }: ActionFunctionArgs) => {\n const { admin } = await authenticate.admin(request);\n\n try {\n const response = await admin.graphql(\n `#graphql\n query incorrectQuery {\n products(first: 10) {\n nodes {\n not_a_field\n }\n }\n }`,\n );\n\n return json({ data: await response.json() });\n } catch (error) {\n if (error instanceof GraphqlQueryError) {\n // error.body.errors:\n // { graphQLErrors: [\n // { message: \"Field 'not_a_field' doesn't exist on type 'Product'\" }\n // ] }\n return json({ errors: error.body?.errors }, { status: 500 });\n }\n return json({ message: \"An error occurred\" }, { status: 500 });\n }\n}", "language": "typescript" }, { "title": "/app/shopify.server.ts", - "code": "import { shopifyApp } from \"@shopify/shopify-app-remix/server\";\n\nconst shopify = shopifyApp({\n restResources,\n // ...etc\n});\nexport default shopify;\nexport const unauthenticated = shopify.unauthenticated;", + "code": "import { shopifyApp } from \"@shopify/shopify-app-remix/server\";\n\nconst shopify = shopifyApp({\n // ...\n});\nexport default shopify;\nexport const authenticate = shopify.authenticate;", "language": "typescript" } ] @@ -10271,39 +10693,163 @@ "title": "app/routes/**\\/.ts" } ] + }, + { + "title": "Handling GraphQL errors", + "description": "Catch `GraphqlQueryError` errors to see error messages from the API.", + "tabs": [ + { + "code": "import { ActionFunctionArgs } from \"@remix-run/node\";\nimport { authenticate } from \"../shopify.server\";\n\nexport const action = async ({ request }: ActionFunctionArgs) => {\n const { storefront } = await authenticate.public.appProxy(request);\n\n try {\n const response = await storefront.graphql(\n `#graphql\n query incorrectQuery {\n products(first: 10) {\n nodes {\n not_a_field\n }\n }\n }`,\n );\n\n return json({ data: await response.json() });\n } catch (error) {\n if (error instanceof GraphqlQueryError) {\n // { errors: { graphQLErrors: [\n // { message: \"Field 'not_a_field' doesn't exist on type 'Product'\" }\n // ] } }\n return json({ errors: error.body?.errors }, { status: 500 });\n }\n return json({ message: \"An error occurred\" }, { status: 500 });\n }\n}", + "title": "/app/routes/**\\/*.ts" + }, + { + "code": "import { shopifyApp } from \"@shopify/shopify-app-remix/server\";\n\nconst shopify = shopifyApp({\n // ...\n});\nexport default shopify;\nexport const authenticate = shopify.authenticate;", + "title": "/app/shopify.server.ts" + } + ] } ] } ], - "value": "export interface StorefrontContext {\n /**\n * Method for interacting with the Shopify Storefront GraphQL API\n *\n * 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).\n *\n * {@link https://shopify.dev/docs/api/storefront}\n *\n * @example\n * Querying the GraphQL API.\n * Use `storefront.graphql` to make query / mutation requests.\n * ```ts\n * // app/routes/**\\/.ts\n * import { json } from \"@remix-run/node\";\n * import { authenticate } from \"../shopify.server\";\n *\n * export async function action({ request }: ActionFunctionArgs) {\n * const { storefront } = await authenticate.public.appProxy(request);\n *\n * const response = await storefront.graphql(`{blogs(first: 10) { edges { node { id } } } }`);\n *\n * return json(await response.json());\n * }\n * ```\n */\n graphql: GraphQLClient;\n}" - }, - "StorefrontOperations": { - "filePath": "../../node_modules/@shopify/shopify-api/node_modules/@shopify/storefront-api-client/dist/storefront-api-client.d.ts", - "syntaxKind": "TypeAliasDeclaration", - "name": "StorefrontOperations", - "value": "StorefrontQueries & StorefrontMutations", - "description": "" + "value": "export interface StorefrontContext {\n /**\n * Method for interacting with the Shopify Storefront GraphQL API\n *\n * 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).\n *\n * {@link https://shopify.dev/docs/api/storefront}\n *\n * @example\n * Querying the GraphQL API.\n * Use `storefront.graphql` to make query / mutation requests.\n * ```ts\n * // app/routes/**\\/.ts\n * import { json } from \"@remix-run/node\";\n * import { authenticate } from \"../shopify.server\";\n *\n * export async function action({ request }: ActionFunctionArgs) {\n * const { storefront } = await authenticate.public.appProxy(request);\n *\n * const response = await storefront.graphql(`{blogs(first: 10) { edges { node { id } } } }`);\n *\n * return json(await response.json());\n * }\n * ```\n *\n * @example\n * Handling GraphQL errors.\n * Catch `GraphqlQueryError` errors to see error messages from the API.\n * ```ts\n * // /app/routes/**\\/*.ts\n * import { ActionFunctionArgs } from \"@remix-run/node\";\n * import { authenticate } from \"../shopify.server\";\n *\n * export const action = async ({ request }: ActionFunctionArgs) => {\n * const { storefront } = await authenticate.public.appProxy(request);\n *\n * try {\n * const response = await storefront.graphql(\n * `#graphql\n * query incorrectQuery {\n * products(first: 10) {\n * nodes {\n * not_a_field\n * }\n * }\n * }`,\n * );\n *\n * return json({ data: await response.json() });\n * } catch (error) {\n * if (error instanceof GraphqlQueryError) {\n * // { errors: { graphQLErrors: [\n * // { message: \"Field 'not_a_field' doesn't exist on type 'Product'\" }\n * // ] } }\n * return json({ errors: error.body?.errors }, { status: 500 });\n * }\n * return json({ message: \"An error occurred\" }, { status: 500 });\n * }\n * }\n * ```\n *\n * ```ts\n * // /app/shopify.server.ts\n * import { shopifyApp } from \"@shopify/shopify-app-remix/server\";\n *\n * const shopify = shopifyApp({\n * // ...\n * });\n * export default shopify;\n * export const authenticate = shopify.authenticate;\n * ```\n */\n graphql: GraphQLClient;\n}" }, - "StorefrontQueries": { - "filePath": "../../node_modules/@shopify/shopify-api/node_modules/@shopify/storefront-api-client/dist/storefront-api-client.d.ts", - "name": "StorefrontQueries", + "GraphQLClient": { + "filePath": "src/server/clients/types.ts", + "name": "GraphQLClient", "description": "", - "members": [ + "params": [ { - "filePath": "../../node_modules/@shopify/shopify-api/node_modules/@shopify/storefront-api-client/dist/storefront-api-client.d.ts", - "name": "[key: string]", - "value": "{\n variables: any;\n return: any;\n }" + "name": "query", + "description": "", + "value": "Operation extends keyof Operations", + "filePath": "src/server/clients/types.ts" }, { - "filePath": "../../node_modules/@shopify/shopify-api/node_modules/@shopify/storefront-api-client/dist/storefront-api-client.d.ts", - "name": "[key: number | symbol]", - "value": "never" + "name": "options", + "description": "", + "value": "GraphQLQueryOptions", + "isOptional": true, + "filePath": "src/server/clients/types.ts" } ], - "value": "interface StorefrontQueries {\n [key: string]: {\n variables: any;\n return: any;\n };\n [key: number | symbol]: never;\n}" + "returns": { + "filePath": "src/server/clients/types.ts", + "description": "", + "name": "interface Promise {\n /**\n * Attaches callbacks for the resolution and/or rejection of the Promise.\n * @param onfulfilled The callback to execute when the Promise is resolved.\n * @param onrejected The callback to execute when the Promise is rejected.\n * @returns A Promise for the completion of which ever callback is executed.\n */\n then(onfulfilled?: ((value: T) => TResult1 | PromiseLike) | undefined | null, onrejected?: ((reason: any) => TResult2 | PromiseLike) | undefined | null): Promise;\n\n /**\n * Attaches a callback for only the rejection of the Promise.\n * @param onrejected The callback to execute when the Promise is rejected.\n * @returns A Promise for the completion of the callback.\n */\n catch(onrejected?: ((reason: any) => TResult | PromiseLike) | undefined | null): Promise;\n}, interface Promise {}, Promise: PromiseConstructor, interface Promise {\n readonly [Symbol.toStringTag]: string;\n}, interface Promise {\n /**\n * Attaches a callback that is invoked when the Promise is settled (fulfilled or rejected). The\n * resolved value cannot be modified from the callback.\n * @param onfinally The callback to execute when the Promise is settled (fulfilled or rejected).\n * @returns A Promise for the completion of the callback.\n */\n finally(onfinally?: (() => void) | undefined | null): Promise;\n}", + "value": "interface Promise {\n /**\n * Attaches callbacks for the resolution and/or rejection of the Promise.\n * @param onfulfilled The callback to execute when the Promise is resolved.\n * @param onrejected The callback to execute when the Promise is rejected.\n * @returns A Promise for the completion of which ever callback is executed.\n */\n then(onfulfilled?: ((value: T) => TResult1 | PromiseLike) | undefined | null, onrejected?: ((reason: any) => TResult2 | PromiseLike) | undefined | null): Promise;\n\n /**\n * Attaches a callback for only the rejection of the Promise.\n * @param onrejected The callback to execute when the Promise is rejected.\n * @returns A Promise for the completion of the callback.\n */\n catch(onrejected?: ((reason: any) => TResult | PromiseLike) | undefined | null): Promise;\n}, interface Promise {}, Promise: PromiseConstructor, interface Promise {\n readonly [Symbol.toStringTag]: string;\n}, interface Promise {\n /**\n * Attaches a callback that is invoked when the Promise is settled (fulfilled or rejected). The\n * resolved value cannot be modified from the callback.\n * @param onfinally The callback to execute when the Promise is settled (fulfilled or rejected).\n * @returns A Promise for the completion of the callback.\n */\n finally(onfinally?: (() => void) | undefined | null): Promise;\n}" + }, + "value": "export type GraphQLClient = <\n Operation extends keyof Operations,\n>(\n query: Operation,\n options?: GraphQLQueryOptions,\n) => Promise>;" }, - "StorefrontMutations": { - "filePath": "../../node_modules/@shopify/shopify-api/node_modules/@shopify/storefront-api-client/dist/storefront-api-client.d.ts", + "GraphQLQueryOptions": { + "filePath": "src/server/clients/types.ts", + "name": "GraphQLQueryOptions", + "description": "", + "members": [ + { + "filePath": "src/server/clients/types.ts", + "syntaxKind": "PropertySignature", + "name": "variables", + "value": "ApiClientRequestOptions[\"variables\"]", + "description": "The variables to pass to the operation.", + "isOptional": true + }, + { + "filePath": "src/server/clients/types.ts", + "syntaxKind": "PropertySignature", + "name": "apiVersion", + "value": "ApiVersion", + "description": "The version of the API to use for the request.", + "isOptional": true + }, + { + "filePath": "src/server/clients/types.ts", + "syntaxKind": "PropertySignature", + "name": "headers", + "value": "Record", + "description": "Additional headers to include in the request.", + "isOptional": true + }, + { + "filePath": "src/server/clients/types.ts", + "syntaxKind": "PropertySignature", + "name": "tries", + "value": "number", + "description": "The total number of times to try the request if it fails.", + "isOptional": true + } + ], + "value": "export interface GraphQLQueryOptions<\n Operation extends keyof Operations,\n Operations extends AllOperations,\n> {\n /**\n * The variables to pass to the operation.\n */\n variables?: ApiClientRequestOptions['variables'];\n /**\n * The version of the API to use for the request.\n */\n apiVersion?: ApiVersion;\n /**\n * Additional headers to include in the request.\n */\n headers?: Record;\n /**\n * The total number of times to try the request if it fails.\n */\n tries?: number;\n}" + }, + "ApiVersion": { + "filePath": "../../node_modules/@shopify/shopify-api/lib/types.d.ts", + "syntaxKind": "EnumDeclaration", + "name": "ApiVersion", + "value": "export declare enum ApiVersion {\n October22 = \"2022-10\",\n January23 = \"2023-01\",\n April23 = \"2023-04\",\n July23 = \"2023-07\",\n October23 = \"2023-10\",\n January24 = \"2024-01\",\n Unstable = \"unstable\"\n}", + "members": [ + { + "filePath": "../../node_modules/@shopify/shopify-api/lib/types.d.ts", + "name": "October22", + "value": "2022-10" + }, + { + "filePath": "../../node_modules/@shopify/shopify-api/lib/types.d.ts", + "name": "January23", + "value": "2023-01" + }, + { + "filePath": "../../node_modules/@shopify/shopify-api/lib/types.d.ts", + "name": "April23", + "value": "2023-04" + }, + { + "filePath": "../../node_modules/@shopify/shopify-api/lib/types.d.ts", + "name": "July23", + "value": "2023-07" + }, + { + "filePath": "../../node_modules/@shopify/shopify-api/lib/types.d.ts", + "name": "October23", + "value": "2023-10" + }, + { + "filePath": "../../node_modules/@shopify/shopify-api/lib/types.d.ts", + "name": "January24", + "value": "2024-01" + }, + { + "filePath": "../../node_modules/@shopify/shopify-api/lib/types.d.ts", + "name": "Unstable", + "value": "unstable" + } + ] + }, + "StorefrontOperations": { + "filePath": "../../node_modules/@shopify/shopify-api/node_modules/@shopify/storefront-api-client/dist/storefront-api-client.d.ts", + "syntaxKind": "TypeAliasDeclaration", + "name": "StorefrontOperations", + "value": "StorefrontQueries & StorefrontMutations", + "description": "" + }, + "StorefrontQueries": { + "filePath": "../../node_modules/@shopify/shopify-api/node_modules/@shopify/storefront-api-client/dist/storefront-api-client.d.ts", + "name": "StorefrontQueries", + "description": "", + "members": [ + { + "filePath": "../../node_modules/@shopify/shopify-api/node_modules/@shopify/storefront-api-client/dist/storefront-api-client.d.ts", + "name": "[key: string]", + "value": "{\n variables: any;\n return: any;\n }" + }, + { + "filePath": "../../node_modules/@shopify/shopify-api/node_modules/@shopify/storefront-api-client/dist/storefront-api-client.d.ts", + "name": "[key: number | symbol]", + "value": "never" + } + ], + "value": "interface StorefrontQueries {\n [key: string]: {\n variables: any;\n return: any;\n };\n [key: number | symbol]: never;\n}" + }, + "StorefrontMutations": { + "filePath": "../../node_modules/@shopify/shopify-api/node_modules/@shopify/storefront-api-client/dist/storefront-api-client.d.ts", "name": "StorefrontMutations", "description": "", "members": [ @@ -10356,6 +10902,24 @@ } ] } + }, + { + "description": "Catch `GraphqlQueryError` errors to see error messages from the API.", + "codeblock": { + "title": "Handling GraphQL errors", + "tabs": [ + { + "title": "/app/routes/**\\/*.ts", + "code": "import { ActionFunctionArgs } from \"@remix-run/node\";\nimport { authenticate } from \"../shopify.server\";\n\nexport const action = async ({ request }: ActionFunctionArgs) => {\n const { storefront } = await authenticate.public.appProxy(request);\n\n try {\n const response = await storefront.graphql(\n `#graphql\n query incorrectQuery {\n products(first: 10) {\n nodes {\n not_a_field\n }\n }\n }`,\n );\n\n return json({ data: await response.json() });\n } catch (error) {\n if (error instanceof GraphqlQueryError) {\n // { errors: { graphQLErrors: [\n // { message: \"Field 'not_a_field' doesn't exist on type 'Product'\" }\n // ] } }\n return json({ errors: error.body?.errors }, { status: 500 });\n }\n return json({ message: \"An error occurred\" }, { status: 500 });\n }\n}", + "language": "typescript" + }, + { + "title": "/app/shopify.server.ts", + "code": "import { shopifyApp } from \"@shopify/shopify-app-remix/server\";\n\nconst shopify = shopifyApp({\n // ...\n});\nexport default shopify;\nexport const authenticate = shopify.authenticate;", + "language": "typescript" + } + ] + } } ] } @@ -10382,7 +10946,7 @@ { "name": "appConfig", "description": "Configuration options for your Shopify app, such as the scopes your app needs.", - "value": "Config extends AppConfigArg", + "value": "Readonly", "filePath": "src/server/shopify-app.ts" } ], @@ -10392,7 +10956,7 @@ "name": "ShopifyApp>", "value": "ShopifyApp>" }, - "value": "export function shopifyApp<\n Config extends AppConfigArg,\n Resources extends ShopifyRestResources,\n Storage extends SessionStorage,\n Future extends FutureFlagOptions = Config['future'],\n>(appConfig: Config): ShopifyApp {\n const api = deriveApi(appConfig);\n const config = deriveConfig(appConfig, api.config);\n const logger = overrideLogger(api.logger);\n\n if (appConfig.webhooks) {\n api.webhooks.addHandlers(appConfig.webhooks);\n }\n\n const params: BasicParams = {api, config, logger};\n const authStrategy = authStrategyFactory({\n ...params,\n strategy:\n config.future.unstable_newEmbeddedAuthStrategy && config.isEmbeddedApp\n ? new TokenExchangeStrategy(params)\n : new AuthCodeFlowStrategy(params),\n });\n\n const shopify:\n | AdminApp\n | AppStoreApp\n | SingleMerchantApp = {\n sessionStorage: config.sessionStorage,\n addDocumentResponseHeaders: addDocumentResponseHeadersFactory(params),\n registerWebhooks: registerWebhooksFactory(params),\n authenticate: {\n admin: authStrategy,\n flow: authenticateFlowFactory(params),\n public: authenticatePublicFactory(params),\n webhook: authenticateWebhookFactory<\n Future,\n Resources,\n keyof Config['webhooks'] | MandatoryTopics\n >(params),\n },\n unauthenticated: {\n admin: unauthenticatedAdminContextFactory(params),\n storefront: unauthenticatedStorefrontContextFactory(params),\n },\n };\n\n if (\n isAppStoreApp(shopify, appConfig) ||\n isSingleMerchantApp(shopify, appConfig)\n ) {\n shopify.login = loginFactory(params);\n }\n\n return shopify as ShopifyApp;\n}", + "value": "export function shopifyApp<\n Config extends AppConfigArg,\n Resources extends ShopifyRestResources,\n Storage extends SessionStorage,\n Future extends FutureFlagOptions = Config['future'],\n>(appConfig: Readonly): ShopifyApp {\n const api = deriveApi(appConfig);\n const config = deriveConfig(appConfig, api.config);\n const logger = overrideLogger(api.logger);\n\n if (appConfig.webhooks) {\n api.webhooks.addHandlers(appConfig.webhooks);\n }\n\n const params: BasicParams = {api, config, logger};\n const authStrategy = authStrategyFactory({\n ...params,\n strategy:\n config.future.unstable_newEmbeddedAuthStrategy && config.isEmbeddedApp\n ? new TokenExchangeStrategy(params)\n : new AuthCodeFlowStrategy(params),\n });\n\n const shopify:\n | AdminApp\n | AppStoreApp\n | SingleMerchantApp = {\n sessionStorage: config.sessionStorage,\n addDocumentResponseHeaders: addDocumentResponseHeadersFactory(params),\n registerWebhooks: registerWebhooksFactory(params),\n authenticate: {\n admin: authStrategy,\n flow: authenticateFlowFactory(params),\n public: authenticatePublicFactory(params),\n webhook: authenticateWebhookFactory<\n Future,\n Resources,\n keyof Config['webhooks'] | MandatoryTopics\n >(params),\n },\n unauthenticated: {\n admin: unauthenticatedAdminContextFactory(params),\n storefront: unauthenticatedStorefrontContextFactory(params),\n },\n };\n\n if (\n isAppStoreApp(shopify, appConfig) ||\n isSingleMerchantApp(shopify, appConfig)\n ) {\n shopify.login = loginFactory(params);\n }\n\n return shopify as ShopifyApp;\n}", "examples": [ { "title": "The minimum viable configuration", @@ -10406,60 +10970,114 @@ } ] }, - "AppConfigArg": { - "filePath": "src/server/config-types.ts", - "name": "AppConfigArg", + "Readonly": { + "filePath": "../../node_modules/@shopify/polaris/build/ts/src/components/TextField/TextField.d.ts", + "name": "Readonly", "description": "", "members": [ { - "filePath": "src/server/config-types.ts", + "filePath": "../../node_modules/@shopify/polaris/build/ts/src/components/TextField/TextField.d.ts", "syntaxKind": "PropertySignature", - "name": "appUrl", - "value": "string", - "description": "The URL your app is running on.\n\nThe `@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." + "name": "readonly", + "value": "true", + "description": "", + "isOptional": true + } + ], + "value": "interface Readonly {\n readonly?: true;\n}" + }, + "ShopifyApp": { + "filePath": "src/server/types.ts", + "syntaxKind": "TypeAliasDeclaration", + "name": "ShopifyApp", + "value": "Config['distribution'] extends AppDistribution.ShopifyAdmin\n ? AdminApp\n : Config['distribution'] extends AppDistribution.SingleMerchant\n ? SingleMerchantApp\n : Config['distribution'] extends AppDistribution.AppStore\n ? AppStoreApp\n : AppStoreApp", + "description": "An object your app can use to interact with Shopify.\n\nBy default, the app's distribution is `AppStore`." + }, + "AppDistribution": { + "filePath": "src/server/types.ts", + "syntaxKind": "EnumDeclaration", + "name": "AppDistribution", + "value": "export enum AppDistribution {\n AppStore = 'app_store',\n SingleMerchant = 'single_merchant',\n ShopifyAdmin = 'shopify_admin',\n}", + "members": [ + { + "filePath": "src/server/types.ts", + "name": "AppStore", + "value": "app_store" }, { - "filePath": "src/server/config-types.ts", + "filePath": "src/server/types.ts", + "name": "SingleMerchant", + "value": "single_merchant" + }, + { + "filePath": "src/server/types.ts", + "name": "ShopifyAdmin", + "value": "shopify_admin" + } + ] + }, + "AdminApp": { + "filePath": "src/server/types.ts", + "syntaxKind": "TypeAliasDeclaration", + "name": "AdminApp", + "value": "ShopifyAppBase", + "description": "", + "members": [ + { + "filePath": "src/server/types.ts", "syntaxKind": "PropertySignature", "name": "sessionStorage", - "value": "Storage", - "description": "An adaptor for storing sessions in your database of choice.\n\nShopify provides multiple session storage adaptors and you can create your own.\n\n\n\n\n", + "value": "SessionStorageType", + "description": "The `SessionStorage` instance you passed in as a config option.", "examples": [ { "title": "Storing sessions with Prisma", - "description": "Add the `@shopify/shopify-app-session-storage-prisma` package to use the Prisma session storage.", + "description": "Import the `@shopify/shopify-app-session-storage-prisma` package to store sessions in your Prisma database.", "tabs": [ { - "code": "import { shopifyApp } from \"@shopify/shopify-app-remix/server\";\nimport { PrismaSessionStorage } from \"@shopify/shopify-app-session-storage-prisma\";\n\nimport prisma from \"~/db.server\";\n\nconst shopify = shopifyApp({\n // ... etc\n sessionStorage: new PrismaSessionStorage(prisma),\n});\nexport default shopify;", - "title": "Example" + "code": "import { shopifyApp } from \"@shopify/shopify-app-remix/server\";\nimport { PrismaSessionStorage } from \"@shopify/shopify-app-session-storage-prisma\";\nimport prisma from \"~/db.server\";\n\nconst shopify = shopifyApp({\n sessionStorage: new PrismaSessionStorage(prisma),\n // ...etc\n})\n\n// shopify.sessionStorage is an instance of PrismaSessionStorage", + "title": "/app/shopify.server.ts" } ] } ] }, { - "filePath": "src/server/config-types.ts", + "filePath": "src/server/types.ts", "syntaxKind": "PropertySignature", - "name": "useOnlineTokens", - "value": "boolean", - "description": "Whether your app use online or offline tokens.\n\nIf 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.\n\n\n\n\n", - "isOptional": true, - "defaultValue": "`false`" + "name": "addDocumentResponseHeaders", + "value": "AddDocumentResponseHeaders", + "description": "Adds the required Content Security Policy headers for Shopify apps to the given Headers object.\n\n\n\n\n", + "examples": [ + { + "title": "Return headers on all requests", + "description": "Add headers to all HTML requests by calling `shopify.addDocumentResponseHeaders` in `entry.server.tsx`.", + "tabs": [ + { + "code": "import { shopifyApp } from \"@shopify/shopify-app-remix/server\";\n\nconst shopify = shopifyApp({\n // ...etc\n});\nexport default shopify;\nexport const addDocumentResponseheaders = shopify.addDocumentResponseheaders;", + "title": "~/shopify.server.ts" + }, + { + "code": "import { addDocumentResponseHeaders } from \"~/shopify.server\";\n\nexport default function handleRequest(\n request: Request,\n responseStatusCode: number,\n responseHeaders: Headers,\n remixContext: EntryContext\n) {\n const markup = renderToString(\n \n );\n\n responseHeaders.set(\"Content-Type\", \"text/html\");\n addDocumentResponseHeaders(request, responseHeaders);\n\n return new Response(\"\" + markup, {\n status: responseStatusCode,\n headers: responseHeaders,\n });\n}", + "title": "entry.server.tsx" + } + ] + } + ] }, { - "filePath": "src/server/config-types.ts", + "filePath": "src/server/types.ts", "syntaxKind": "PropertySignature", - "name": "webhooks", - "value": "WebhookConfig", - "description": "The config for the webhook topics your app would like to subscribe to.\n\n\n\n\n\n\n\nThis can be in used in conjunction with the afterAuth hook to register webhook topics when a user installs your app. Or you can use this function in other processes such as background jobs.", - "isOptional": true, + "name": "registerWebhooks", + "value": "RegisterWebhooks", + "description": "Register webhook topics for a store using the given session. Most likely you want to use this in combination with the afterAuth hook.", "examples": [ { - "title": "Registering for a webhook when a merchant uninstalls your app", - "description": "", + "title": "Registering webhooks after install", + "description": "Trigger the registration to create the webhook subscriptions after a merchant installs your app using the `afterAuth` hook. Learn more about [subscribing to webhooks.](/docs/api/shopify-app-remix/v1/guide-webhooks)", "tabs": [ { - "code": "import { DeliveryMethod, shopifyApp } from \"@shopify/shopify-app-remix/server\";\n\nconst shopify = shopifyApp({\n webhooks: {\n APP_UNINSTALLED: {\n deliveryMethod: DeliveryMethod.Http,\n callbackUrl: \"/webhooks\",\n },\n },\n hooks: {\n afterAuth: async ({ session }) => {\n shopify.registerWebhooks({ session });\n }\n },\n // ...etc\n});\nexport default shopify;\nexport const authenticate = shopify.authenticate;\n\n// /app/routes/webhooks.jsx\nimport { ActionFunctionArgs } from \"@remix-run/node\";\n\nimport { authenticate } from \"../shopify.server\";\nimport db from \"../db.server\";\n\nexport const action = async ({ request }: ActionFunctionArgs) => {\n const { topic, shop } = await authenticate.webhook(request);\n\n switch (topic) {\n case \"APP_UNINSTALLED\":\n await db.session.deleteMany({ where: { shop } });\n break;\n case \"CUSTOMERS_DATA_REQUEST\":\n case \"CUSTOMERS_REDACT\":\n case \"SHOP_REDACT\":\n default:\n throw new Response(\"Unhandled webhook topic\", { status: 404 });\n }\n throw new Response();\n};", + "code": "import { DeliveryMethod, shopifyApp } from \"@shopify/shopify-app-remix/server\";\n\nconst shopify = shopifyApp({\n hooks: {\n afterAuth: async ({ session }) => {\n shopify.registerWebhooks({ session });\n }\n },\n webhooks: {\n APP_UNINSTALLED: {\n deliveryMethod: DeliveryMethod.Http,\n callbackUrl: \"/webhooks\",\n },\n },\n // ...etc\n});", "title": "/app/shopify.server.ts" } ] @@ -10467,231 +11085,128 @@ ] }, { - "filePath": "src/server/config-types.ts", + "filePath": "src/server/types.ts", "syntaxKind": "PropertySignature", - "name": "hooks", - "value": "HooksConfig", - "description": "Functions to call at key places during your apps lifecycle.\n\nThese functions are called in the context of the request that triggered them. This means you can access the session.", - "isOptional": true, + "name": "authenticate", + "value": "Authenticate", + "description": "Ways to authenticate requests from different surfaces across Shopify.", "examples": [ { - "title": "Seeding your database custom data when a merchant installs your app", - "description": "", + "title": "Authenticate Shopify requests", + "description": "Use the functions in `authenticate` to validate requests coming from Shopify.", "tabs": [ { - "code": "import { DeliveryMethod, shopifyApp } from \"@shopify/shopify-app-remix/server\";\nimport { seedStoreData } from \"~/db/seeds\"\n\nconst shopify = shopifyApp({\n hooks: {\n afterAuth: async ({ session }) => {\n seedStoreData({session})\n }\n },\n // ...etc\n});", - "title": "Example" + "code": "import { LATEST_API_VERSION, shopifyApp } from \"@shopify/shopify-app-remix/server\";\nimport { restResources } from \"@shopify/shopify-api/rest/admin/2023-04\";\n\nconst shopify = shopifyApp({\n restResources,\n // ...etc\n});\nexport default shopify;", + "title": "/app/shopify.server.ts" + }, + { + "code": "import { LoaderFunctionArgs, json } from \"@remix-run/node\";\nimport shopify from \"../../shopify.server\";\n\nexport async function loader({ request }: LoaderFunctionArgs) {\n const {admin, session, sessionToken, billing} = shopify.authenticate.admin(request);\n\n return json(await admin.rest.resources.Product.count({ session }));\n}", + "title": "/app/routes/**\\/*.jsx" } ] } ] }, { - "filePath": "src/server/config-types.ts", - "syntaxKind": "PropertySignature", - "name": "isEmbeddedApp", - "value": "boolean", - "description": "Does your app render embedded inside the Shopify Admin or on its own.\n\nUnless you have very specific needs, this should be true.", - "isOptional": true, - "defaultValue": "`true`" - }, - { - "filePath": "src/server/config-types.ts", + "filePath": "src/server/types.ts", "syntaxKind": "PropertySignature", - "name": "distribution", - "value": "AppDistribution", - "description": "How your app is distributed. Default is `AppDistribution.AppStore`.\n\n\n\n\n", - "isOptional": true - }, - { - "filePath": "src/server/config-types.ts", - "syntaxKind": "PropertySignature", - "name": "apiVersion", - "value": "ApiVersion", - "description": "What version of Shopify's Admin API's would you like to use.\n\n\n\n\n", - "isOptional": true, - "defaultValue": "`LATEST_API_VERSION` from `@shopify/shopify-app-remix`", - "examples": [ - { - "title": "Using the latest API Version (Recommended)", - "description": "", - "tabs": [ - { - "code": "import { LATEST_API_VERSION, shopifyApp } from \"@shopify/shopify-app-remix/server\";\n\nconst shopify = shopifyApp({\n // ...etc\n apiVersion: LATEST_API_VERSION,\n});", - "title": "Example" - } - ] - } - ] - }, - { - "filePath": "src/server/config-types.ts", - "syntaxKind": "PropertySignature", - "name": "authPathPrefix", - "value": "string", - "description": "A path that Shopify can reserve for auth related endpoints.\n\nThis must match a $ route in your Remix app. That route must export a loader function that calls `shopify.authenticate.admin(request)`.", - "isOptional": true, - "defaultValue": "`\"/auth\"`", + "name": "unauthenticated", + "value": "Unauthenticated>", + "description": "Ways to get Contexts from requests that do not originate from Shopify.", "examples": [ { - "title": "Using the latest API Version (Recommended)", - "description": "", + "title": "Using unauthenticated contexts", + "description": "Create contexts for requests that don't come from Shopify.", "tabs": [ { - "code": "import { LATEST_API_VERSION, shopifyApp } from \"@shopify/shopify-app-remix/server\";\n\nconst shopify = shopifyApp({\n // ...etc\n apiVersion: LATEST_API_VERSION,\n});\nexport default shopify;\nexport const authenticate = shopify.authenticate;\n\n// /app/routes/auth/$.jsx\nimport { LoaderFunctionArgs } from \"@remix-run/node\";\nimport { authenticate } from \"../../shopify.server\";\n\nexport async function loader({ request }: LoaderFunctionArgs) {\n await authenticate.admin(request);\n\n return null\n}", + "code": "import { LATEST_API_VERSION, shopifyApp } from \"@shopify/shopify-app-remix/server\";\nimport { restResources } from \"@shopify/shopify-api/rest/admin/2023-04\";\n\nconst shopify = shopifyApp({\n restResources,\n // ...etc\n});\nexport default shopify;", "title": "/app/shopify.server.ts" + }, + { + "code": "import { LoaderFunctionArgs, json } from \"@remix-run/node\";\nimport { authenticateExternal } from \"~/helpers/authenticate\"\nimport shopify from \"../../shopify.server\";\n\nexport async function loader({ request }: LoaderFunctionArgs) {\n const shop = await authenticateExternal(request)\n const {admin} = await shopify.unauthenticated.admin(shop);\n\n return json(await admin.rest.resources.Product.count({ session }));\n}", + "title": "/app/routes/**\\/*.jsx" } ] } ] - }, - { - "filePath": "src/server/config-types.ts", - "syntaxKind": "PropertySignature", - "name": "future", - "value": "Future", - "description": "Features that will be introduced in future releases of this package.\n\nYou can opt in to these features by setting the corresponding flags. By doing so, you can prepare for future releases in advance and provide feedback on the new features.", - "isOptional": true - }, - { - "filePath": "src/server/config-types.ts", - "syntaxKind": "PropertySignature", - "name": "apiKey", - "value": "string", - "description": "The API key for your app.\n\nAlso known as Client ID in your Partner Dashboard.", - "isOptional": true - }, - { - "filePath": "src/server/config-types.ts", - "syntaxKind": "PropertySignature", - "name": "apiSecretKey", - "value": "string", - "description": "The API secret key for your app.\n\nAlso known as Client Secret in your Partner Dashboard." - }, - { - "filePath": "src/server/config-types.ts", - "syntaxKind": "PropertySignature", - "name": "scopes", - "value": "string[] | AuthScopes", - "description": "The scopes your app needs to access the API.", - "isOptional": true - }, - { - "filePath": "src/server/config-types.ts", - "syntaxKind": "PropertySignature", - "name": "adminApiAccessToken", - "value": "string", - "description": "An app-wide API access token.\n\nOnly applies to custom apps.", - "isOptional": true - }, - { - "filePath": "src/server/config-types.ts", - "syntaxKind": "PropertySignature", - "name": "userAgentPrefix", - "value": "string", - "description": "The user agent prefix to use for API requests.", - "isOptional": true - }, - { - "filePath": "src/server/config-types.ts", - "syntaxKind": "PropertySignature", - "name": "privateAppStorefrontAccessToken", - "value": "string", - "description": "An app-wide API access token for the storefront API.\n\nOnly applies to custom apps.", - "isOptional": true - }, - { - "filePath": "src/server/config-types.ts", - "syntaxKind": "PropertySignature", - "name": "customShopDomains", - "value": "(string | RegExp)[]", - "description": "Override values for Shopify shop domains.", - "isOptional": true - }, - { - "filePath": "src/server/config-types.ts", - "syntaxKind": "PropertySignature", - "name": "billing", - "value": "BillingConfig", - "description": "Billing configurations for the app.", - "isOptional": true - }, + } + ] + }, + "SessionStorageType": { + "filePath": "src/server/types.ts", + "syntaxKind": "TypeAliasDeclaration", + "name": "SessionStorageType", + "value": "Config['sessionStorage'] extends SessionStorage\n ? Config['sessionStorage']\n : SessionStorage", + "description": "" + }, + "AddDocumentResponseHeaders": { + "filePath": "src/server/types.ts", + "name": "AddDocumentResponseHeaders", + "description": "", + "params": [ { - "filePath": "src/server/config-types.ts", - "syntaxKind": "PropertySignature", - "name": "restResources", - "value": "Resources", - "description": "REST resources to access the Admin API.\n\nYou can import these from `@shopify/shopify-api/rest/admin/*`.", - "isOptional": true + "name": "request", + "description": "", + "value": "Request", + "filePath": "src/server/types.ts" }, { - "filePath": "src/server/config-types.ts", - "syntaxKind": "PropertySignature", - "name": "logger", - "value": "{ log?: LogFunction; level?: LogSeverity; httpRequests?: boolean; timestamps?: boolean; }", - "description": "Customization options for Shopify logs.", - "isOptional": true + "name": "headers", + "description": "", + "value": "Headers", + "filePath": "src/server/types.ts" } ], - "value": "export interface AppConfigArg<\n Resources extends ShopifyRestResources = ShopifyRestResources,\n Storage extends SessionStorage = SessionStorage,\n Future extends FutureFlagOptions = FutureFlagOptions,\n> extends Omit<\n ApiConfigArg>,\n | 'hostName'\n | 'hostScheme'\n | 'isEmbeddedApp'\n | 'apiVersion'\n | 'isCustomStoreApp'\n | 'future'\n > {\n /**\n * The URL your app is running on.\n *\n * 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.\n */\n appUrl: string;\n\n /**\n * An adaptor for storing sessions in your database of choice.\n *\n * Shopify provides multiple session storage adaptors and you can create your own.\n *\n * {@link https://github.com/Shopify/shopify-app-js/blob/main/README.md#session-storage-options}\n *\n * @example\n * Storing sessions with Prisma.\n * Add the `@shopify/shopify-app-session-storage-prisma` package to use the Prisma session storage.\n * ```ts\n * import { shopifyApp } from \"@shopify/shopify-app-remix/server\";\n * import { PrismaSessionStorage } from \"@shopify/shopify-app-session-storage-prisma\";\n *\n * import prisma from \"~/db.server\";\n *\n * const shopify = shopifyApp({\n * // ... etc\n * sessionStorage: new PrismaSessionStorage(prisma),\n * });\n * export default shopify;\n * ```\n */\n sessionStorage: Storage;\n\n /**\n * Whether your app use online or offline tokens.\n *\n * 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.\n *\n * {@link https://shopify.dev/docs/apps/auth/oauth/access-modes}\n *\n * @defaultValue `false`\n */\n useOnlineTokens?: boolean;\n\n /**\n * The config for the webhook topics your app would like to subscribe to.\n *\n * {@link https://shopify.dev/docs/apps/webhooks}\n *\n * This can be in used in conjunction with the afterAuth hook to register webhook topics when a user installs your app. Or you can use this function in other processes such as background jobs.\n *\n * @example\n * Registering for a webhook when a merchant uninstalls your app.\n * ```ts\n * // /app/shopify.server.ts\n * import { DeliveryMethod, shopifyApp } from \"@shopify/shopify-app-remix/server\";\n *\n * const shopify = shopifyApp({\n * webhooks: {\n * APP_UNINSTALLED: {\n * deliveryMethod: DeliveryMethod.Http,\n * callbackUrl: \"/webhooks\",\n * },\n * },\n * hooks: {\n * afterAuth: async ({ session }) => {\n * shopify.registerWebhooks({ session });\n * }\n * },\n * // ...etc\n * });\n * export default shopify;\n * export const authenticate = shopify.authenticate;\n *\n * // /app/routes/webhooks.jsx\n * import { ActionFunctionArgs } from \"@remix-run/node\";\n *\n * import { authenticate } from \"../shopify.server\";\n * import db from \"../db.server\";\n *\n * export const action = async ({ request }: ActionFunctionArgs) => {\n * const { topic, shop } = await authenticate.webhook(request);\n *\n * switch (topic) {\n * case \"APP_UNINSTALLED\":\n * await db.session.deleteMany({ where: { shop } });\n * break;\n * case \"CUSTOMERS_DATA_REQUEST\":\n * case \"CUSTOMERS_REDACT\":\n * case \"SHOP_REDACT\":\n * default:\n * throw new Response(\"Unhandled webhook topic\", { status: 404 });\n * }\n * throw new Response();\n * };\n * ```\n */\n webhooks?: WebhookConfig;\n\n /**\n * Functions to call at key places during your apps lifecycle.\n *\n * These functions are called in the context of the request that triggered them. This means you can access the session.\n *\n * @example\n * Seeding your database custom data when a merchant installs your app.\n * ```ts\n * import { DeliveryMethod, shopifyApp } from \"@shopify/shopify-app-remix/server\";\n * import { seedStoreData } from \"~/db/seeds\"\n *\n * const shopify = shopifyApp({\n * hooks: {\n * afterAuth: async ({ session }) => {\n * seedStoreData({session})\n * }\n * },\n * // ...etc\n * });\n * ```\n */\n hooks?: HooksConfig;\n\n /**\n * Does your app render embedded inside the Shopify Admin or on its own.\n *\n * Unless you have very specific needs, this should be true.\n *\n * @defaultValue `true`\n */\n isEmbeddedApp?: boolean;\n\n /**\n * How your app is distributed. Default is `AppDistribution.AppStore`.\n *\n * {@link https://shopify.dev/docs/apps/distribution}\n */\n distribution?: AppDistribution;\n\n /**\n * What version of Shopify's Admin API's would you like to use.\n *\n * {@link https://shopify.dev/docs/api/}\n *\n * @defaultValue `LATEST_API_VERSION` from `@shopify/shopify-app-remix`\n *\n * @example\n * Using the latest API Version (Recommended)\n * ```ts\n * import { LATEST_API_VERSION, shopifyApp } from \"@shopify/shopify-app-remix/server\";\n *\n * const shopify = shopifyApp({\n * // ...etc\n * apiVersion: LATEST_API_VERSION,\n * });\n * ```\n */\n apiVersion?: ApiVersion;\n\n /**\n * A path that Shopify can reserve for auth related endpoints.\n *\n * This must match a $ route in your Remix app. That route must export a loader function that calls `shopify.authenticate.admin(request)`.\n *\n * @default `\"/auth\"`\n *\n * @example\n * Using the latest API Version (Recommended)\n * ```ts\n * // /app/shopify.server.ts\n * import { LATEST_API_VERSION, shopifyApp } from \"@shopify/shopify-app-remix/server\";\n *\n * const shopify = shopifyApp({\n * // ...etc\n * apiVersion: LATEST_API_VERSION,\n * });\n * export default shopify;\n * export const authenticate = shopify.authenticate;\n *\n * // /app/routes/auth/$.jsx\n * import { LoaderFunctionArgs } from \"@remix-run/node\";\n * import { authenticate } from \"../../shopify.server\";\n *\n * export async function loader({ request }: LoaderFunctionArgs) {\n * await authenticate.admin(request);\n *\n * return null\n * }\n * ```\n */\n authPathPrefix?: string;\n\n /**\n * Features that will be introduced in future releases of this package.\n *\n * You can opt in to these features by setting the corresponding flags. By doing so, you can prepare for future\n * releases in advance and provide feedback on the new features.\n */\n future?: Future;\n}" + "returns": { + "filePath": "src/server/types.ts", + "description": "", + "name": "void", + "value": "void" + }, + "value": "type AddDocumentResponseHeaders = (request: Request, headers: Headers) => void;" }, - "WebhookConfig": { - "filePath": "src/server/config-types.ts", + "Headers": { + "filePath": "../../node_modules/@shopify/shopify-api/runtime/http/types.d.ts", "syntaxKind": "TypeAliasDeclaration", - "name": "WebhookConfig", - "value": "Record", + "name": "Headers", + "value": "Record", "description": "", "members": [] }, - "HooksConfig": { - "filePath": "src/server/config-types.ts", - "name": "HooksConfig", + "RegisterWebhooks": { + "filePath": "src/server/types.ts", + "name": "RegisterWebhooks", "description": "", - "members": [ + "params": [ { - "filePath": "src/server/config-types.ts", - "syntaxKind": "PropertySignature", - "name": "afterAuth", - "value": "(options: AfterAuthOptions) => void | Promise", - "description": "A function to call after a merchant installs your app", - "isOptional": true, - "examples": [ - { - "title": "Registering webhooks and seeding data when a merchant installs your app", - "description": "", - "tabs": [ - { - "code": "import { DeliveryMethod, shopifyApp } from \"@shopify/shopify-app-remix/server\";\nimport { seedStoreData } from \"~/db/seeds\"\n\nconst shopify = shopifyApp({\n hooks: {\n afterAuth: async ({ session }) => {\n shopify.registerWebhooks({ session });\n seedStoreData({session})\n }\n },\n webhooks: {\n APP_UNINSTALLED: {\n deliveryMethod: DeliveryMethod.Http,\n callbackUrl: \"/webhooks\",\n },\n },\n // ...etc\n});", - "title": "Example" - } - ] - } - ] + "name": "options", + "description": "", + "value": "RegisterWebhooksOptions", + "filePath": "src/server/types.ts" } ], - "value": "interface HooksConfig {\n /**\n * A function to call after a merchant installs your app\n *\n * @param context - An object with context about the request that triggered the hook.\n * @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.\n * @param context.admin - An object with access to the Shopify Admin API's.\n *\n * @example\n * Registering webhooks and seeding data when a merchant installs your app.\n * ```ts\n * import { DeliveryMethod, shopifyApp } from \"@shopify/shopify-app-remix/server\";\n * import { seedStoreData } from \"~/db/seeds\"\n *\n * const shopify = shopifyApp({\n * hooks: {\n * afterAuth: async ({ session }) => {\n * shopify.registerWebhooks({ session });\n * seedStoreData({session})\n * }\n * },\n * webhooks: {\n * APP_UNINSTALLED: {\n * deliveryMethod: DeliveryMethod.Http,\n * callbackUrl: \"/webhooks\",\n * },\n * },\n * // ...etc\n * });\n * ```\n */\n afterAuth?: (options: AfterAuthOptions) => void | Promise;\n}" + "returns": { + "filePath": "src/server/types.ts", + "description": "", + "name": "Promise", + "value": "Promise" + }, + "value": "type RegisterWebhooks = (\n options: RegisterWebhooksOptions,\n) => Promise;" }, - "AfterAuthOptions": { - "filePath": "src/server/config-types.ts", - "name": "AfterAuthOptions", + "RegisterWebhooksOptions": { + "filePath": "src/server/authenticate/webhooks/types.ts", + "name": "RegisterWebhooksOptions", "description": "", "members": [ { - "filePath": "src/server/config-types.ts", + "filePath": "src/server/authenticate/webhooks/types.ts", "syntaxKind": "PropertySignature", "name": "session", "value": "Session", - "description": "" - }, - { - "filePath": "src/server/config-types.ts", - "syntaxKind": "PropertySignature", - "name": "admin", - "value": "AdminApiContext", - "description": "" + "description": "The Shopify session used to register webhooks using the Admin API." } ], - "value": "export interface AfterAuthOptions<\n R extends ShopifyRestResources = ShopifyRestResources,\n> {\n session: Session;\n admin: AdminApiContext;\n}" + "value": "export interface RegisterWebhooksOptions {\n /**\n * The Shopify session used to register webhooks using the Admin API.\n */\n session: Session;\n}" }, "Session": { "filePath": "../../node_modules/@shopify/shopify-api/lib/session/session.d.ts", @@ -10932,38 +11447,247 @@ ], "value": "export interface SessionParams {\n /**\n * The unique identifier for the session.\n */\n readonly id: string;\n /**\n * The Shopify shop domain.\n */\n shop: string;\n /**\n * The state of the session. Used for the OAuth authentication code flow.\n */\n state: string;\n /**\n * Whether the access token in the session is online or offline.\n */\n isOnline: boolean;\n /**\n * The scopes for the access token.\n */\n scope?: string;\n /**\n * The date the access token expires.\n */\n expires?: Date;\n /**\n * The access token for the session.\n */\n accessToken?: string;\n /**\n * Information on the user for the session. Only present for online sessions.\n */\n onlineAccessInfo?: OnlineAccessInfo;\n}" }, - "AdminApiContext": { - "filePath": "src/server/clients/admin/types.ts", - "name": "AdminApiContext", + "RegisterReturn": { + "filePath": "../../node_modules/@shopify/shopify-api/lib/webhooks/types.d.ts", + "syntaxKind": "TypeAliasDeclaration", + "name": "RegisterReturn", + "value": "Record", + "description": "", + "members": [] + }, + "Authenticate": { + "filePath": "src/server/types.ts", + "name": "Authenticate", "description": "", "members": [ { - "filePath": "src/server/clients/admin/types.ts", + "filePath": "src/server/types.ts", "syntaxKind": "PropertySignature", - "name": "rest", - "value": "RestClientWithResources", - "description": "Methods for interacting with the Shopify Admin REST API\n\nThere are methods for interacting with individual REST resources. You can also make `GET`, `POST`, `PUT` and `DELETE` requests should the REST resources not meet your needs.\n\n\n\n\n", + "name": "admin", + "value": "AuthenticateAdmin>", + "description": "Authenticate an admin Request and get back an authenticated admin context. Use the authenticated admin context to interact with Shopify.\n\nExamples of when to use this are requests from your app's UI, or requests from admin extensions.\n\nIf there is no session for the Request, this will redirect the merchant to correct auth flows.", "examples": [ { - "title": "Using REST resources", - "description": "Getting the number of orders in a store using REST resources. Visit the [Admin REST API references](/docs/api/admin-rest) for examples on using each resource.", + "title": "Authenticating a request for an embedded app", + "description": "", "tabs": [ { - "code": "import { LoaderFunctionArgs, json } from \"@remix-run/node\";\nimport { authenticate } from \"../shopify.server\";\n\nexport const loader = async ({ request }: LoaderFunctionArgs) => {\n const { admin, session } = await authenticate.admin(request);\n return json(admin.rest.resources.Order.count({ session }));\n};", - "title": "/app/routes/**\\/*.ts" + "code": "import { LoaderFunctionArgs, json } from \"@remix-run/node\";\nimport { authenticate } from \"../../shopify.server\";\n\nexport async function loader({ request }: LoaderFunctionArgs) {\n const {admin, session, sessionToken, billing} = authenticate.admin(request);\n\n return json(await admin.rest.resources.Product.count({ session }));\n}", + "title": "/app/routes/**\\/*.jsx" }, { - "code": "import { shopifyApp } from \"@shopify/shopify-app-remix/server\";\nimport { restResources } from \"@shopify/shopify-api/rest/admin/2023-07\";\n\nconst shopify = shopifyApp({\n restResources,\n // ...etc\n});\nexport default shopify;\nexport const authenticate = shopify.authenticate;", + "code": "import { LATEST_API_VERSION, shopifyApp } from \"@shopify/shopify-app-remix/server\";\nimport { restResources } from \"@shopify/shopify-api/rest/admin/2023-04\";\n\nconst shopify = shopifyApp({\n restResources,\n // ...etc\n});\nexport default shopify;\nexport const authenticate = shopify.authenticate;", "title": "/app/shopify.server.ts" } ] - }, - { + } + ] + }, + { + "filePath": "src/server/types.ts", + "syntaxKind": "PropertySignature", + "name": "flow", + "value": "AuthenticateFlow>", + "description": "Authenticate a Flow extension Request and get back an authenticated context, containing an admin context to access the API, and the payload of the request.\n\nIf there is no session for the Request, this will return an HTTP 400 error.\n\nNote that this will always be a POST request.", + "examples": [ + { + "title": "Authenticating a Flow extension request", + "description": "", + "tabs": [ + { + "code": "import { ActionFunctionArgs, json } from \"@remix-run/node\";\nimport { authenticate } from \"../../shopify.server\";\n\nexport async function action({ request }: ActionFunctionArgs) {\n const {admin, session, payload} = authenticate.flow(request);\n\n // Perform flow extension logic\n\n // Return a 200 response\n return null;\n}", + "title": "/app/routes/**\\/*.jsx" + }, + { + "code": "import { LATEST_API_VERSION, shopifyApp } from \"@shopify/shopify-app-remix/server\";\nimport { restResources } from \"@shopify/shopify-api/rest/admin/2023-04\";\n\nconst shopify = shopifyApp({\n restResources,\n // ...etc\n});\nexport default shopify;\nexport const authenticate = shopify.authenticate;", + "title": "/app/shopify.server.ts" + } + ] + } + ] + }, + { + "filePath": "src/server/types.ts", + "syntaxKind": "PropertySignature", + "name": "public", + "value": "AuthenticatePublic", + "description": "Authenticate a public request and get back a session token.", + "examples": [ + { + "title": "Authenticating a request from a checkout extension", + "description": "", + "tabs": [ + { + "code": "import { LoaderFunctionArgs, json } from \"@remix-run/node\";\nimport { authenticate } from \"../../shopify.server\";\nimport { getWidgets } from \"~/db/widgets\";\n\nexport async function loader({ request }: LoaderFunctionArgs) {\n const {sessionToken} = authenticate.public.checkout(request);\n\n return json(await getWidgets(sessionToken));\n}", + "title": "/app/routes/api/checkout.jsx" + } + ] + } + ] + }, + { + "filePath": "src/server/types.ts", + "syntaxKind": "PropertySignature", + "name": "webhook", + "value": "AuthenticateWebhook<\n Config['future'],\n RestResourcesType,\n keyof Config['webhooks'] | MandatoryTopics\n >", + "description": "Authenticate a Shopify webhook request, get back an authenticated admin context and details on the webhook request", + "examples": [ + { + "title": "Authenticating a webhook request", + "description": "", + "tabs": [ + { + "code": "import { DeliveryMethod, shopifyApp } from \"@shopify/shopify-app-remix/server\";\n\nconst shopify = shopifyApp({\n webhooks: {\n APP_UNINSTALLED: {\n deliveryMethod: DeliveryMethod.Http,\n callbackUrl: \"/webhooks\",\n },\n },\n hooks: {\n afterAuth: async ({ session }) => {\n shopify.registerWebhooks({ session });\n },\n },\n // ...etc\n});\nexport default shopify;\nexport const authenticate = shopify.authenticate;", + "title": "/app/shopify.server.ts" + }, + { + "code": "import { ActionFunctionArgs } from \"@remix-run/node\";\nimport { authenticate } from \"../shopify.server\";\nimport db from \"../db.server\";\n\nexport const action = async ({ request }: ActionFunctionArgs) => {\n const { topic, shop, session } = await authenticate.webhook(request);\n\n switch (topic) {\n case \"APP_UNINSTALLED\":\n if (session) {\n await db.session.deleteMany({ where: { shop } });\n }\n break;\n case \"CUSTOMERS_DATA_REQUEST\":\n case \"CUSTOMERS_REDACT\":\n case \"SHOP_REDACT\":\n default:\n throw new Response(\"Unhandled webhook topic\", { status: 404 });\n }\n\n throw new Response();\n};", + "title": "/app/routes/webhooks.ts" + } + ] + } + ] + } + ], + "value": "interface Authenticate {\n /**\n * Authenticate an admin Request and get back an authenticated admin context. Use the authenticated admin context to interact with Shopify.\n *\n * Examples of when to use this are requests from your app's UI, or requests from admin extensions.\n *\n * If there is no session for the Request, this will redirect the merchant to correct auth flows.\n *\n * @example\n * Authenticating a request for an embedded app.\n * ```ts\n * // /app/routes/**\\/*.jsx\n * import { LoaderFunctionArgs, json } from \"@remix-run/node\";\n * import { authenticate } from \"../../shopify.server\";\n *\n * export async function loader({ request }: LoaderFunctionArgs) {\n * const {admin, session, sessionToken, billing} = authenticate.admin(request);\n *\n * return json(await admin.rest.resources.Product.count({ session }));\n * }\n * ```\n * ```ts\n * // /app/shopify.server.ts\n * import { LATEST_API_VERSION, shopifyApp } from \"@shopify/shopify-app-remix/server\";\n * import { restResources } from \"@shopify/shopify-api/rest/admin/2023-04\";\n *\n * const shopify = shopifyApp({\n * restResources,\n * // ...etc\n * });\n * export default shopify;\n * export const authenticate = shopify.authenticate;\n * ```\n */\n admin: AuthenticateAdmin>;\n\n /**\n * Authenticate a Flow extension Request and get back an authenticated context, containing an admin context to access\n * the API, and the payload of the request.\n *\n * If there is no session for the Request, this will return an HTTP 400 error.\n *\n * Note that this will always be a POST request.\n *\n * @example\n * Authenticating a Flow extension request.\n * ```ts\n * // /app/routes/**\\/*.jsx\n * import { ActionFunctionArgs, json } from \"@remix-run/node\";\n * import { authenticate } from \"../../shopify.server\";\n *\n * export async function action({ request }: ActionFunctionArgs) {\n * const {admin, session, payload} = authenticate.flow(request);\n *\n * // Perform flow extension logic\n *\n * // Return a 200 response\n * return null;\n * }\n * ```\n * ```ts\n * // /app/shopify.server.ts\n * import { LATEST_API_VERSION, shopifyApp } from \"@shopify/shopify-app-remix/server\";\n * import { restResources } from \"@shopify/shopify-api/rest/admin/2023-04\";\n *\n * const shopify = shopifyApp({\n * restResources,\n * // ...etc\n * });\n * export default shopify;\n * export const authenticate = shopify.authenticate;\n * ```\n */\n flow: AuthenticateFlow>;\n\n /**\n * Authenticate a public request and get back a session token.\n *\n * @example\n * Authenticating a request from a checkout extension\n *\n * ```ts\n * // /app/routes/api/checkout.jsx\n * import { LoaderFunctionArgs, json } from \"@remix-run/node\";\n * import { authenticate } from \"../../shopify.server\";\n * import { getWidgets } from \"~/db/widgets\";\n *\n * export async function loader({ request }: LoaderFunctionArgs) {\n * const {sessionToken} = authenticate.public.checkout(request);\n *\n * return json(await getWidgets(sessionToken));\n * }\n * ```\n */\n public: AuthenticatePublic;\n\n /**\n * Authenticate a Shopify webhook request, get back an authenticated admin context and details on the webhook request\n *\n * @example\n * Authenticating a webhook request\n *\n * ```ts\n * // /app/shopify.server.ts\n * import { DeliveryMethod, shopifyApp } from \"@shopify/shopify-app-remix/server\";\n *\n * const shopify = shopifyApp({\n * webhooks: {\n * APP_UNINSTALLED: {\n * deliveryMethod: DeliveryMethod.Http,\n * callbackUrl: \"/webhooks\",\n * },\n * },\n * hooks: {\n * afterAuth: async ({ session }) => {\n * shopify.registerWebhooks({ session });\n * },\n * },\n * // ...etc\n * });\n * export default shopify;\n * export const authenticate = shopify.authenticate;\n * ```\n * ```ts\n * // /app/routes/webhooks.ts\n * import { ActionFunctionArgs } from \"@remix-run/node\";\n * import { authenticate } from \"../shopify.server\";\n * import db from \"../db.server\";\n *\n * export const action = async ({ request }: ActionFunctionArgs) => {\n * const { topic, shop, session } = await authenticate.webhook(request);\n *\n * switch (topic) {\n * case \"APP_UNINSTALLED\":\n * if (session) {\n * await db.session.deleteMany({ where: { shop } });\n * }\n * break;\n * case \"CUSTOMERS_DATA_REQUEST\":\n * case \"CUSTOMERS_REDACT\":\n * case \"SHOP_REDACT\":\n * default:\n * throw new Response(\"Unhandled webhook topic\", { status: 404 });\n * }\n *\n * throw new Response();\n * };\n * ```\n */\n webhook: AuthenticateWebhook<\n Config['future'],\n RestResourcesType,\n keyof Config['webhooks'] | MandatoryTopics\n >;\n}" + }, + "AuthenticateAdmin": { + "filePath": "src/server/authenticate/admin/types.ts", + "name": "AuthenticateAdmin", + "description": "", + "params": [ + { + "name": "request", + "description": "", + "value": "Request", + "filePath": "src/server/authenticate/admin/types.ts" + } + ], + "returns": { + "filePath": "src/server/authenticate/admin/types.ts", + "description": "", + "name": "Promise>", + "value": "Promise>" + }, + "value": "export type AuthenticateAdmin<\n Config extends AppConfigArg,\n Resources extends ShopifyRestResources = ShopifyRestResources,\n> = (request: Request) => Promise>;" + }, + "AdminContext": { + "filePath": "src/server/authenticate/admin/types.ts", + "syntaxKind": "TypeAliasDeclaration", + "name": "AdminContext", + "value": "Config['isEmbeddedApp'] extends false\n ? NonEmbeddedAdminContext\n : EmbeddedAdminContext", + "description": "" + }, + "NonEmbeddedAdminContext": { + "filePath": "src/server/authenticate/admin/types.ts", + "name": "NonEmbeddedAdminContext", + "description": "", + "members": [ + { + "filePath": "src/server/authenticate/admin/types.ts", + "syntaxKind": "PropertySignature", + "name": "session", + "value": "Session", + "description": "The session for the user who made the request.\n\nThis comes from the session storage which `shopifyApp` uses to store sessions in your database of choice.\n\nUse this to get shop or user-specific data.", + "examples": [ + { + "title": "Using offline sessions", + "description": "Get your app's shop-specific data using an offline session.", + "tabs": [ + { + "code": "import { LoaderFunctionArgs, json } from \"@remix-run/node\";\nimport { authenticate } from \"../shopify.server\";\nimport { getMyAppData } from \"~/db/model.server\";\n\nexport const loader = async ({ request }: LoaderFunctionArgs) => {\n const { session } = await authenticate.admin(request);\n return json(await getMyAppData({shop: session.shop));\n};", + "title": "/app/routes/**\\/*.ts" + }, + { + "code": "import { shopifyApp } from \"@shopify/shopify-app-remix/server\";\n\nconst shopify = shopifyApp({\n // ...etc\n});\nexport default shopify;\nexport const authenticate = shopify.authenticate;", + "title": "shopify.server.ts" + } + ] + }, + { + "title": "Using online sessions", + "description": "Get your app's user-specific data using an online session.", + "tabs": [ + { + "code": "import { LoaderFunctionArgs, json } from \"@remix-run/node\";\nimport { authenticate } from \"../shopify.server\";\nimport { getMyAppData } from \"~/db/model.server\";\n\nexport const loader = async ({ request }: LoaderFunctionArgs) => {\n const { session } = await authenticate.admin(request);\n return json(await getMyAppData({user: session.onlineAccessInfo!.id}));\n};", + "title": "/app/routes/**\\/*.ts" + }, + { + "code": "import { shopifyApp } from \"@shopify/shopify-app-remix/server\";\n\nconst shopify = shopifyApp({\n // ...etc\n useOnlineTokens: true,\n});\nexport default shopify;\nexport const authenticate = shopify.authenticate;", + "title": "shopify.server.ts" + } + ] + } + ] + }, + { + "filePath": "src/server/authenticate/admin/types.ts", + "syntaxKind": "PropertySignature", + "name": "admin", + "value": "AdminApiContext", + "description": "Methods for interacting with the GraphQL / REST Admin APIs for the store that made the request." + }, + { + "filePath": "src/server/authenticate/admin/types.ts", + "syntaxKind": "PropertySignature", + "name": "billing", + "value": "BillingContext", + "description": "Billing methods for this store, based on the plans defined in the `billing` config option.\n\n\n\n\n" + }, + { + "filePath": "src/server/authenticate/admin/types.ts", + "syntaxKind": "PropertySignature", + "name": "cors", + "value": "EnsureCORSFunction", + "description": "A function that ensures the CORS headers are set correctly for the response.", + "examples": [ + { + "title": "Setting CORS headers for a admin request", + "description": "Use the `cors` helper to ensure your app can respond to requests from admin extensions.", + "tabs": [ + { + "code": "import { LoaderFunctionArgs, json } from \"@remix-run/node\";\nimport { authenticate } from \"../shopify.server\";\nimport { getMyAppData } from \"~/db/model.server\";\n\nexport const loader = async ({ request }: LoaderFunctionArgs) => {\n const { session, cors } = await authenticate.admin(request);\n return cors(json(await getMyAppData({user: session.onlineAccessInfo!.id})));\n};", + "title": "/app/routes/admin/my-route.ts" + } + ] + } + ] + } + ], + "value": "export interface NonEmbeddedAdminContext<\n Config extends AppConfigArg,\n Resources extends ShopifyRestResources = ShopifyRestResources,\n> extends AdminContextInternal {}" + }, + "AdminApiContext": { + "filePath": "src/server/clients/admin/types.ts", + "name": "AdminApiContext", + "description": "", + "members": [ + { + "filePath": "src/server/clients/admin/types.ts", + "syntaxKind": "PropertySignature", + "name": "rest", + "value": "RestClientWithResources", + "description": "Methods for interacting with the Shopify Admin REST API\n\nThere are methods for interacting with individual REST resources. You can also make `GET`, `POST`, `PUT` and `DELETE` requests should the REST resources not meet your needs.\n\n\n\n\n", + "examples": [ + { + "title": "Using REST resources", + "description": "Getting the number of orders in a store using REST resources. Visit the [Admin REST API references](/docs/api/admin-rest) for examples on using each resource.", + "tabs": [ + { + "code": "import { LoaderFunctionArgs, json } from \"@remix-run/node\";\nimport { authenticate } from \"../shopify.server\";\n\nexport const loader = async ({ request }: LoaderFunctionArgs) => {\n const {\n admin,\n session,\n } = await authenticate.admin(request);\n\n return json(\n admin.rest.resources.Order.count({ session }),\n );\n};", + "title": "/app/routes/**\\/*.ts" + }, + { + "code": "import { shopifyApp } from \"@shopify/shopify-app-remix/server\";\nimport { restResources } from \"@shopify/shopify-api/rest/admin/2023-07\";\n\nconst shopify = shopifyApp({\n restResources,\n // ...etc\n});\nexport default shopify;\nexport const authenticate = shopify.authenticate;", + "title": "/app/shopify.server.ts" + } + ] + }, + { "title": "Performing a GET request to the REST API", "description": "Use `admin.rest.get` to make custom requests to make a request to to the `customer/count` endpoint", "tabs": [ { - "code": "import { LoaderFunctionArgs, json } from \"@remix-run/node\";\nimport { authenticate } from \"../shopify.server\";\n\nexport const loader = async ({ request }: LoaderFunctionArgs) => {\n const { admin, session } = await authenticate.admin(request);\n const response = await admin.rest.get({ path: \"/customers/count.json\" });\n const customers = await response.json();\n return json({ customers });\n};", + "code": "import { LoaderFunctionArgs, json } from \"@remix-run/node\";\nimport { authenticate } from \"../shopify.server\";\n\nexport const loader = async ({ request }: LoaderFunctionArgs) => {\n const {\n admin,\n session,\n } = await authenticate.admin(request);\n\n const response = await admin.rest.get({\n path: \"/customers/count.json\",\n });\n const customers = await response.json();\n\n return json({ customers });\n};", "title": "/app/routes/**\\/*.ts" }, { @@ -10977,7 +11701,7 @@ "description": "Use `admin.rest.post` to make custom requests to make a request to to the `customers.json` endpoint to send a welcome email", "tabs": [ { - "code": "import { LoaderFunctionArgs, json } from \"@remix-run/node\";\nimport { authenticate } from \"../shopify.server\";\n\nexport const loader = async ({ request }: LoaderFunctionArgs) => {\n const { admin, session } = await authenticate.admin(request);\n const response = admin.rest.post({\n path: \"customers/7392136888625/send_invite.json\",\n body: {\n customer_invite: {\n to: \"new_test_email@shopify.com\",\n from: \"j.limited@example.com\",\n bcc: [\"j.limited@example.com\"],\n subject: \"Welcome to my new shop\",\n custom_message: \"My awesome new store\",\n },\n },\n});\n const customerInvite = await response.json();\n return json({ customerInvite });\n};", + "code": "import { LoaderFunctionArgs, json } from \"@remix-run/node\";\nimport { authenticate } from \"../shopify.server\";\n\nexport const loader = async ({ request }: LoaderFunctionArgs) => {\n const {\n admin,\n session,\n } = await authenticate.admin(request);\n\n const response = admin.rest.post({\n path: \"customers/7392136888625/send_invite.json\",\n body: {\n customer_invite: {\n to: \"new_test_email@shopify.com\",\n from: \"j.limited@example.com\",\n bcc: [\"j.limited@example.com\"],\n subject: \"Welcome to my new shop\",\n custom_message: \"My awesome new store\",\n },\n },\n });\n\n const customerInvite = await response.json();\n return json({ customerInvite });\n};", "title": "/app/routes/**\\/*.ts" }, { @@ -11000,11 +11724,25 @@ "description": "Use `admin.graphql` to make query / mutation requests.", "tabs": [ { - "code": "import { ActionFunctionArgs } from \"@remix-run/node\";\nimport { authenticate } from \"../shopify.server\";\n\nexport async function action({ request }: ActionFunctionArgs) {\n const { admin } = await authenticate.admin(request);\n\n const response = await admin.graphql(\n `#graphql\n mutation populateProduct($input: ProductInput!) {\n productCreate(input: $input) {\n product {\n id\n }\n }\n }`,\n { variables: { input: { title: \"Product Name\" } } }\n );\n\n const productData = await response.json();\n return json({ data: productData.data });\n}", - "title": "Example" + "code": "import { ActionFunctionArgs } from \"@remix-run/node\";\nimport { authenticate } from \"../shopify.server\";\n\nexport const action = async ({ request }: ActionFunctionArgs) => {\n const { admin } = await authenticate.admin(request);\n\n const response = await admin.graphql(\n `#graphql\n mutation populateProduct($input: ProductInput!) {\n productCreate(input: $input) {\n product {\n id\n }\n }\n }`,\n {\n variables: {\n input: { title: \"Product Name\" },\n },\n },\n );\n\n const productData = await response.json();\n return json({\n productId: productData.data?.productCreate?.product?.id,\n });\n}", + "title": "/app/routes/**\\/*.ts" + }, + { + "code": "import { shopifyApp } from \"@shopify/shopify-app-remix/server\";\n\nconst shopify = shopifyApp({\n // ...\n});\nexport default shopify;\nexport const authenticate = shopify.authenticate;", + "title": "/app/shopify.server.ts" + } + ] + }, + { + "title": "Handling GraphQL errors", + "description": "Catch `GraphqlQueryError` errors to see error messages from the API.", + "tabs": [ + { + "code": "import { ActionFunctionArgs } from \"@remix-run/node\";\nimport { authenticate } from \"../shopify.server\";\n\nexport const action = async ({ request }: ActionFunctionArgs) => {\n const { admin } = await authenticate.admin(request);\n\n try {\n const response = await admin.graphql(\n `#graphql\n query incorrectQuery {\n products(first: 10) {\n nodes {\n not_a_field\n }\n }\n }`,\n );\n\n return json({ data: await response.json() });\n } catch (error) {\n if (error instanceof GraphqlQueryError) {\n // error.body.errors:\n // { graphQLErrors: [\n // { message: \"Field 'not_a_field' doesn't exist on type 'Product'\" }\n // ] }\n return json({ errors: error.body?.errors }, { status: 500 });\n }\n return json({ message: \"An error occurred\" }, { status: 500 });\n }\n}", + "title": "/app/routes/**\\/*.ts" }, { - "code": "import { shopifyApp } from \"@shopify/shopify-app-remix/server\";\n\nconst shopify = shopifyApp({\n restResources,\n // ...etc\n});\nexport default shopify;\nexport const unauthenticated = shopify.unauthenticated;", + "code": "import { shopifyApp } from \"@shopify/shopify-app-remix/server\";\n\nconst shopify = shopifyApp({\n // ...\n});\nexport default shopify;\nexport const authenticate = shopify.authenticate;", "title": "/app/shopify.server.ts" } ] @@ -11012,7 +11750,7 @@ ] } ], - "value": "export interface AdminApiContext<\n Resources extends ShopifyRestResources = ShopifyRestResources,\n> {\n /**\n * Methods for interacting with the Shopify Admin REST API\n *\n * There are methods for interacting with individual REST resources. You can also make `GET`, `POST`, `PUT` and `DELETE` requests should the REST resources not meet your needs.\n *\n * {@link https://shopify.dev/docs/api/admin-rest}\n *\n * @example\n * Using REST resources.\n * Getting the number of orders in a store using REST resources. Visit the [Admin REST API references](/docs/api/admin-rest) for examples on using each resource. \n *\n * ```ts\n * // /app/routes/**\\/*.ts\n * import { LoaderFunctionArgs, json } from \"@remix-run/node\";\n * import { authenticate } from \"../shopify.server\";\n *\n * export const loader = async ({ request }: LoaderFunctionArgs) => {\n * const { admin, session } = await authenticate.admin(request);\n * return json(admin.rest.resources.Order.count({ session }));\n * };\n * ```\n *\n *\n * ```ts\n * // /app/shopify.server.ts\n * import { shopifyApp } from \"@shopify/shopify-app-remix/server\";\n * import { restResources } from \"@shopify/shopify-api/rest/admin/2023-07\";\n *\n * const shopify = shopifyApp({\n * restResources,\n * // ...etc\n * });\n * export default shopify;\n * export const authenticate = shopify.authenticate;\n * ```\n *\n *\n * @example\n * Performing a GET request to the REST API.\n * Use `admin.rest.get` to make custom requests to make a request to to the `customer/count` endpoint\n *\n * ```ts\n * // /app/routes/**\\/*.ts\n * import { LoaderFunctionArgs, json } from \"@remix-run/node\";\n * import { authenticate } from \"../shopify.server\";\n *\n * export const loader = async ({ request }: LoaderFunctionArgs) => {\n * const { admin, session } = await authenticate.admin(request);\n * const response = await admin.rest.get({ path: \"/customers/count.json\" });\n * const customers = await response.json();\n * return json({ customers });\n * };\n * ```\n *\n * ```ts\n * // /app/shopify.server.ts\n * import { shopifyApp } from \"@shopify/shopify-app-remix/server\";\n * import { restResources } from \"@shopify/shopify-api/rest/admin/2023-04\";\n *\n * const shopify = shopifyApp({\n * restResources,\n * // ...etc\n * });\n * export default shopify;\n * export const authenticate = shopify.authenticate;\n * ```\n * @example\n * Performing a POST request to the REST API.\n * Use `admin.rest.post` to make custom requests to make a request to to the `customers.json` endpoint to send a welcome email\n * ```ts\n * // /app/routes/**\\/*.ts\n * import { LoaderFunctionArgs, json } from \"@remix-run/node\";\n * import { authenticate } from \"../shopify.server\";\n *\n * export const loader = async ({ request }: LoaderFunctionArgs) => {\n * const { admin, session } = await authenticate.admin(request);\n * const response = admin.rest.post({\n * path: \"customers/7392136888625/send_invite.json\",\n * body: {\n * customer_invite: {\n * to: \"new_test_email@shopify.com\",\n * from: \"j.limited@example.com\",\n * bcc: [\"j.limited@example.com\"],\n * subject: \"Welcome to my new shop\",\n * custom_message: \"My awesome new store\",\n * },\n * },\n * });\n * const customerInvite = await response.json();\n * return json({ customerInvite });\n * };\n * ```\n *\n * ```ts\n * // /app/shopify.server.ts\n * import { shopifyApp } from \"@shopify/shopify-app-remix/server\";\n * import { restResources } from \"@shopify/shopify-api/rest/admin/2023-04\";\n *\n * const shopify = shopifyApp({\n * restResources,\n * // ...etc\n * });\n * export default shopify;\n * export const authenticate = shopify.authenticate;\n * ```\n */\n rest: RestClientWithResources;\n\n /**\n * Methods for interacting with the Shopify Admin GraphQL API\n *\n * {@link https://shopify.dev/docs/api/admin-graphql}\n * {@link https://github.com/Shopify/shopify-api-js/blob/main/packages/shopify-api/docs/reference/clients/Graphql.md}\n *\n * @example\n * Querying the GraphQL API.\n * Use `admin.graphql` to make query / mutation requests.\n * ```ts\n * import { ActionFunctionArgs } from \"@remix-run/node\";\n * import { authenticate } from \"../shopify.server\";\n *\n * export async function action({ request }: ActionFunctionArgs) {\n * const { admin } = await authenticate.admin(request);\n *\n * const response = await admin.graphql(\n * `#graphql\n * mutation populateProduct($input: ProductInput!) {\n * productCreate(input: $input) {\n * product {\n * id\n * }\n * }\n * }`,\n * { variables: { input: { title: \"Product Name\" } } }\n * );\n *\n * const productData = await response.json();\n * return json({ data: productData.data });\n * }\n * ```\n *\n * ```ts\n * // /app/shopify.server.ts\n * import { shopifyApp } from \"@shopify/shopify-app-remix/server\";\n *\n * const shopify = shopifyApp({\n * restResources,\n * // ...etc\n * });\n * export default shopify;\n * export const unauthenticated = shopify.unauthenticated;\n * ```\n */\n graphql: GraphQLClient;\n}" + "value": "export interface AdminApiContext<\n Resources extends ShopifyRestResources = ShopifyRestResources,\n> {\n /**\n * Methods for interacting with the Shopify Admin REST API\n *\n * There are methods for interacting with individual REST resources. You can also make `GET`, `POST`, `PUT` and `DELETE` requests should the REST resources not meet your needs.\n *\n * {@link https://shopify.dev/docs/api/admin-rest}\n *\n * @example\n * Using REST resources.\n * Getting the number of orders in a store using REST resources. Visit the [Admin REST API references](/docs/api/admin-rest) for examples on using each resource. \n *\n * ```ts\n * // /app/routes/**\\/*.ts\n * import { LoaderFunctionArgs, json } from \"@remix-run/node\";\n * import { authenticate } from \"../shopify.server\";\n *\n * export const loader = async ({ request }: LoaderFunctionArgs) => {\n * const {\n * admin,\n * session,\n * } = await authenticate.admin(request);\n *\n * return json(\n * admin.rest.resources.Order.count({ session }),\n * );\n * };\n * ```\n *\n * ```ts\n * // /app/shopify.server.ts\n * import { shopifyApp } from \"@shopify/shopify-app-remix/server\";\n * import { restResources } from \"@shopify/shopify-api/rest/admin/2023-07\";\n *\n * const shopify = shopifyApp({\n * restResources,\n * // ...etc\n * });\n * export default shopify;\n * export const authenticate = shopify.authenticate;\n * ```\n *\n * @example\n * Performing a GET request to the REST API.\n * Use `admin.rest.get` to make custom requests to make a request to to the `customer/count` endpoint\n *\n * ```ts\n * // /app/routes/**\\/*.ts\n * import { LoaderFunctionArgs, json } from \"@remix-run/node\";\n * import { authenticate } from \"../shopify.server\";\n *\n * export const loader = async ({ request }: LoaderFunctionArgs) => {\n * const {\n * admin,\n * session,\n * } = await authenticate.admin(request);\n *\n * const response = await admin.rest.get({\n * path: \"/customers/count.json\",\n * });\n * const customers = await response.json();\n *\n * return json({ customers });\n * };\n * ```\n *\n * ```ts\n * // /app/shopify.server.ts\n * import { shopifyApp } from \"@shopify/shopify-app-remix/server\";\n * import { restResources } from \"@shopify/shopify-api/rest/admin/2023-04\";\n *\n * const shopify = shopifyApp({\n * restResources,\n * // ...etc\n * });\n * export default shopify;\n * export const authenticate = shopify.authenticate;\n * ```\n *\n * @example\n * Performing a POST request to the REST API.\n * Use `admin.rest.post` to make custom requests to make a request to to the `customers.json` endpoint to send a welcome email\n * ```ts\n * // /app/routes/**\\/*.ts\n * import { LoaderFunctionArgs, json } from \"@remix-run/node\";\n * import { authenticate } from \"../shopify.server\";\n *\n * export const loader = async ({ request }: LoaderFunctionArgs) => {\n * const {\n * admin,\n * session,\n * } = await authenticate.admin(request);\n *\n * const response = admin.rest.post({\n * path: \"customers/7392136888625/send_invite.json\",\n * body: {\n * customer_invite: {\n * to: \"new_test_email@shopify.com\",\n * from: \"j.limited@example.com\",\n * bcc: [\"j.limited@example.com\"],\n * subject: \"Welcome to my new shop\",\n * custom_message: \"My awesome new store\",\n * },\n * },\n * });\n *\n * const customerInvite = await response.json();\n * return json({ customerInvite });\n * };\n * ```\n *\n * ```ts\n * // /app/shopify.server.ts\n * import { shopifyApp } from \"@shopify/shopify-app-remix/server\";\n * import { restResources } from \"@shopify/shopify-api/rest/admin/2023-04\";\n *\n * const shopify = shopifyApp({\n * restResources,\n * // ...etc\n * });\n * export default shopify;\n * export const authenticate = shopify.authenticate;\n * ```\n */\n rest: RestClientWithResources;\n\n /**\n * Methods for interacting with the Shopify Admin GraphQL API\n *\n * {@link https://shopify.dev/docs/api/admin-graphql}\n * {@link https://github.com/Shopify/shopify-api-js/blob/main/packages/shopify-api/docs/reference/clients/Graphql.md}\n *\n * @example\n * Querying the GraphQL API.\n * Use `admin.graphql` to make query / mutation requests.\n * ```ts\n * // /app/routes/**\\/*.ts\n * import { ActionFunctionArgs } from \"@remix-run/node\";\n * import { authenticate } from \"../shopify.server\";\n *\n * export const action = async ({ request }: ActionFunctionArgs) => {\n * const { admin } = await authenticate.admin(request);\n *\n * const response = await admin.graphql(\n * `#graphql\n * mutation populateProduct($input: ProductInput!) {\n * productCreate(input: $input) {\n * product {\n * id\n * }\n * }\n * }`,\n * {\n * variables: {\n * input: { title: \"Product Name\" },\n * },\n * },\n * );\n *\n * const productData = await response.json();\n * return json({\n * productId: productData.data?.productCreate?.product?.id,\n * });\n * }\n * ```\n *\n * ```ts\n * // /app/shopify.server.ts\n * import { shopifyApp } from \"@shopify/shopify-app-remix/server\";\n *\n * const shopify = shopifyApp({\n * // ...\n * });\n * export default shopify;\n * export const authenticate = shopify.authenticate;\n * ```\n *\n * @example\n * Handling GraphQL errors.\n * Catch `GraphqlQueryError` errors to see error messages from the API.\n * ```ts\n * // /app/routes/**\\/*.ts\n * import { ActionFunctionArgs } from \"@remix-run/node\";\n * import { authenticate } from \"../shopify.server\";\n *\n * export const action = async ({ request }: ActionFunctionArgs) => {\n * const { admin } = await authenticate.admin(request);\n *\n * try {\n * const response = await admin.graphql(\n * `#graphql\n * query incorrectQuery {\n * products(first: 10) {\n * nodes {\n * not_a_field\n * }\n * }\n * }`,\n * );\n *\n * return json({ data: await response.json() });\n * } catch (error) {\n * if (error instanceof GraphqlQueryError) {\n * // error.body.errors:\n * // { graphQLErrors: [\n * // { message: \"Field 'not_a_field' doesn't exist on type 'Product'\" }\n * // ] }\n * return json({ errors: error.body?.errors }, { status: 500 });\n * }\n * return json({ message: \"An error occurred\" }, { status: 500 });\n * }\n * }\n * ```\n *\n * ```ts\n * // /app/shopify.server.ts\n * import { shopifyApp } from \"@shopify/shopify-app-remix/server\";\n *\n * const shopify = shopifyApp({\n * // ...\n * });\n * export default shopify;\n * export const authenticate = shopify.authenticate;\n * ```\n */\n graphql: GraphQLClient;\n}" }, "RestClientWithResources": { "filePath": "src/server/clients/admin/rest.ts", @@ -11165,79 +11903,72 @@ "value": "GetRequestParams & {\n data: Record | string;\n}", "description": "" }, - "AdminOperations": { - "filePath": "../../node_modules/@shopify/shopify-api/node_modules/@shopify/admin-api-client/dist/ts/graphql/types.d.ts", - "syntaxKind": "TypeAliasDeclaration", - "name": "AdminOperations", - "value": "AdminQueries & AdminMutations", - "description": "" - }, - "AdminQueries": { - "filePath": "../../node_modules/@shopify/shopify-api/node_modules/@shopify/admin-api-client/dist/ts/graphql/types.d.ts", - "name": "AdminQueries", + "GraphQLClient": { + "filePath": "src/server/clients/types.ts", + "name": "GraphQLClient", "description": "", - "members": [ + "params": [ { - "filePath": "../../node_modules/@shopify/shopify-api/node_modules/@shopify/admin-api-client/dist/ts/graphql/types.d.ts", - "name": "[key: string]", - "value": "{\n variables: any;\n return: any;\n }" + "name": "query", + "description": "", + "value": "Operation extends keyof Operations", + "filePath": "src/server/clients/types.ts" }, { - "filePath": "../../node_modules/@shopify/shopify-api/node_modules/@shopify/admin-api-client/dist/ts/graphql/types.d.ts", - "name": "[key: number | symbol]", - "value": "never" + "name": "options", + "description": "", + "value": "GraphQLQueryOptions", + "isOptional": true, + "filePath": "src/server/clients/types.ts" } ], - "value": "export interface AdminQueries {\n [key: string]: {\n variables: any;\n return: any;\n };\n [key: number | symbol]: never;\n}" + "returns": { + "filePath": "src/server/clients/types.ts", + "description": "", + "name": "interface Promise {\n /**\n * Attaches callbacks for the resolution and/or rejection of the Promise.\n * @param onfulfilled The callback to execute when the Promise is resolved.\n * @param onrejected The callback to execute when the Promise is rejected.\n * @returns A Promise for the completion of which ever callback is executed.\n */\n then(onfulfilled?: ((value: T) => TResult1 | PromiseLike) | undefined | null, onrejected?: ((reason: any) => TResult2 | PromiseLike) | undefined | null): Promise;\n\n /**\n * Attaches a callback for only the rejection of the Promise.\n * @param onrejected The callback to execute when the Promise is rejected.\n * @returns A Promise for the completion of the callback.\n */\n catch(onrejected?: ((reason: any) => TResult | PromiseLike) | undefined | null): Promise;\n}, interface Promise {}, Promise: PromiseConstructor, interface Promise {\n readonly [Symbol.toStringTag]: string;\n}, interface Promise {\n /**\n * Attaches a callback that is invoked when the Promise is settled (fulfilled or rejected). The\n * resolved value cannot be modified from the callback.\n * @param onfinally The callback to execute when the Promise is settled (fulfilled or rejected).\n * @returns A Promise for the completion of the callback.\n */\n finally(onfinally?: (() => void) | undefined | null): Promise;\n}", + "value": "interface Promise {\n /**\n * Attaches callbacks for the resolution and/or rejection of the Promise.\n * @param onfulfilled The callback to execute when the Promise is resolved.\n * @param onrejected The callback to execute when the Promise is rejected.\n * @returns A Promise for the completion of which ever callback is executed.\n */\n then(onfulfilled?: ((value: T) => TResult1 | PromiseLike) | undefined | null, onrejected?: ((reason: any) => TResult2 | PromiseLike) | undefined | null): Promise;\n\n /**\n * Attaches a callback for only the rejection of the Promise.\n * @param onrejected The callback to execute when the Promise is rejected.\n * @returns A Promise for the completion of the callback.\n */\n catch(onrejected?: ((reason: any) => TResult | PromiseLike) | undefined | null): Promise;\n}, interface Promise {}, Promise: PromiseConstructor, interface Promise {\n readonly [Symbol.toStringTag]: string;\n}, interface Promise {\n /**\n * Attaches a callback that is invoked when the Promise is settled (fulfilled or rejected). The\n * resolved value cannot be modified from the callback.\n * @param onfinally The callback to execute when the Promise is settled (fulfilled or rejected).\n * @returns A Promise for the completion of the callback.\n */\n finally(onfinally?: (() => void) | undefined | null): Promise;\n}" + }, + "value": "export type GraphQLClient = <\n Operation extends keyof Operations,\n>(\n query: Operation,\n options?: GraphQLQueryOptions,\n) => Promise>;" }, - "AdminMutations": { - "filePath": "../../node_modules/@shopify/shopify-api/node_modules/@shopify/admin-api-client/dist/ts/graphql/types.d.ts", - "name": "AdminMutations", + "GraphQLQueryOptions": { + "filePath": "src/server/clients/types.ts", + "name": "GraphQLQueryOptions", "description": "", "members": [ { - "filePath": "../../node_modules/@shopify/shopify-api/node_modules/@shopify/admin-api-client/dist/ts/graphql/types.d.ts", - "name": "[key: string]", - "value": "{\n variables: any;\n return: any;\n }" + "filePath": "src/server/clients/types.ts", + "syntaxKind": "PropertySignature", + "name": "variables", + "value": "ApiClientRequestOptions[\"variables\"]", + "description": "The variables to pass to the operation.", + "isOptional": true }, { - "filePath": "../../node_modules/@shopify/shopify-api/node_modules/@shopify/admin-api-client/dist/ts/graphql/types.d.ts", - "name": "[key: number | symbol]", - "value": "never" - } - ], - "value": "export interface AdminMutations {\n [key: string]: {\n variables: any;\n return: any;\n };\n [key: number | symbol]: never;\n}" - }, - "ShopifyRestResources": { - "filePath": "../../node_modules/@shopify/shopify-api/rest/types.d.ts", - "syntaxKind": "TypeAliasDeclaration", - "name": "ShopifyRestResources", - "value": "Record", - "description": "", - "members": [] - }, - "AppDistribution": { - "filePath": "src/server/types.ts", - "syntaxKind": "EnumDeclaration", - "name": "AppDistribution", - "value": "export enum AppDistribution {\n AppStore = 'app_store',\n SingleMerchant = 'single_merchant',\n ShopifyAdmin = 'shopify_admin',\n}", - "members": [ - { - "filePath": "src/server/types.ts", - "name": "AppStore", - "value": "app_store" + "filePath": "src/server/clients/types.ts", + "syntaxKind": "PropertySignature", + "name": "apiVersion", + "value": "ApiVersion", + "description": "The version of the API to use for the request.", + "isOptional": true }, { - "filePath": "src/server/types.ts", - "name": "SingleMerchant", - "value": "single_merchant" + "filePath": "src/server/clients/types.ts", + "syntaxKind": "PropertySignature", + "name": "headers", + "value": "Record", + "description": "Additional headers to include in the request.", + "isOptional": true }, { - "filePath": "src/server/types.ts", - "name": "ShopifyAdmin", - "value": "shopify_admin" + "filePath": "src/server/clients/types.ts", + "syntaxKind": "PropertySignature", + "name": "tries", + "value": "number", + "description": "The total number of times to try the request if it fails.", + "isOptional": true } - ] + ], + "value": "export interface GraphQLQueryOptions<\n Operation extends keyof Operations,\n Operations extends AllOperations,\n> {\n /**\n * The variables to pass to the operation.\n */\n variables?: ApiClientRequestOptions['variables'];\n /**\n * The version of the API to use for the request.\n */\n apiVersion?: ApiVersion;\n /**\n * Additional headers to include in the request.\n */\n headers?: Record;\n /**\n * The total number of times to try the request if it fails.\n */\n tries?: number;\n}" }, "ApiVersion": { "filePath": "../../node_modules/@shopify/shopify-api/lib/types.d.ts", @@ -11282,2127 +12013,1346 @@ } ] }, - "BillingConfig": { - "filePath": "../../node_modules/@shopify/shopify-api/lib/billing/types.d.ts", - "name": "BillingConfig", - "description": "Billing configuration options, indexed by an app-specific plan name.", - "members": [ - { - "filePath": "../../node_modules/@shopify/shopify-api/lib/billing/types.d.ts", - "name": "[plan: string]", - "value": "BillingConfigItem" - } - ], - "value": "export interface BillingConfig {\n /**\n * An individual billing plan.\n */\n [plan: string]: BillingConfigItem;\n}" - }, - "BillingConfigItem": { - "filePath": "../../node_modules/@shopify/shopify-api/lib/billing/types.d.ts", + "AdminOperations": { + "filePath": "../../node_modules/@shopify/shopify-api/node_modules/@shopify/admin-api-client/dist/ts/graphql/types.d.ts", "syntaxKind": "TypeAliasDeclaration", - "name": "BillingConfigItem", - "value": "FeatureEnabled extends true ? BillingConfigOneTimePlan | BillingConfigSubscriptionLineItemPlan : BillingConfigLegacyItem", + "name": "AdminOperations", + "value": "AdminQueries & AdminMutations", "description": "" }, - "BillingConfigOneTimePlan": { - "filePath": "../../node_modules/@shopify/shopify-api/lib/billing/types.d.ts", - "name": "BillingConfigOneTimePlan", + "AdminQueries": { + "filePath": "../../node_modules/@shopify/shopify-api/node_modules/@shopify/admin-api-client/dist/ts/graphql/types.d.ts", + "name": "AdminQueries", "description": "", "members": [ { - "filePath": "../../node_modules/@shopify/shopify-api/lib/billing/types.d.ts", - "syntaxKind": "PropertySignature", - "name": "interval", - "value": "BillingInterval.OneTime", - "description": "Interval for this plan.\n\nMust be set to `OneTime`." - }, - { - "filePath": "../../node_modules/@shopify/shopify-api/lib/billing/types.d.ts", - "syntaxKind": "PropertySignature", - "name": "amount", - "value": "number", - "description": "Amount to charge for this plan." + "filePath": "../../node_modules/@shopify/shopify-api/node_modules/@shopify/admin-api-client/dist/ts/graphql/types.d.ts", + "name": "[key: string]", + "value": "{\n variables: any;\n return: any;\n }" }, { - "filePath": "../../node_modules/@shopify/shopify-api/lib/billing/types.d.ts", - "syntaxKind": "PropertySignature", - "name": "currencyCode", - "value": "string", - "description": "Currency code for this plan." + "filePath": "../../node_modules/@shopify/shopify-api/node_modules/@shopify/admin-api-client/dist/ts/graphql/types.d.ts", + "name": "[key: number | symbol]", + "value": "never" } ], - "value": "export interface BillingConfigOneTimePlan extends BillingConfigPlan {\n /**\n * Interval for this plan.\n *\n * Must be set to `OneTime`.\n */\n interval: BillingInterval.OneTime;\n}" + "value": "export interface AdminQueries {\n [key: string]: {\n variables: any;\n return: any;\n };\n [key: number | symbol]: never;\n}" }, - "BillingInterval": { - "filePath": "../../node_modules/@shopify/shopify-api/lib/types.d.ts", - "syntaxKind": "EnumDeclaration", - "name": "BillingInterval", - "value": "export declare enum BillingInterval {\n OneTime = \"ONE_TIME\",\n Every30Days = \"EVERY_30_DAYS\",\n Annual = \"ANNUAL\",\n Usage = \"USAGE\"\n}", + "AdminMutations": { + "filePath": "../../node_modules/@shopify/shopify-api/node_modules/@shopify/admin-api-client/dist/ts/graphql/types.d.ts", + "name": "AdminMutations", + "description": "", "members": [ { - "filePath": "../../node_modules/@shopify/shopify-api/lib/types.d.ts", - "name": "OneTime", - "value": "ONE_TIME" - }, - { - "filePath": "../../node_modules/@shopify/shopify-api/lib/types.d.ts", - "name": "Every30Days", - "value": "EVERY_30_DAYS" - }, - { - "filePath": "../../node_modules/@shopify/shopify-api/lib/types.d.ts", - "name": "Annual", - "value": "ANNUAL" + "filePath": "../../node_modules/@shopify/shopify-api/node_modules/@shopify/admin-api-client/dist/ts/graphql/types.d.ts", + "name": "[key: string]", + "value": "{\n variables: any;\n return: any;\n }" }, { - "filePath": "../../node_modules/@shopify/shopify-api/lib/types.d.ts", - "name": "Usage", - "value": "USAGE" + "filePath": "../../node_modules/@shopify/shopify-api/node_modules/@shopify/admin-api-client/dist/ts/graphql/types.d.ts", + "name": "[key: number | symbol]", + "value": "never" } - ] + ], + "value": "export interface AdminMutations {\n [key: string]: {\n variables: any;\n return: any;\n };\n [key: number | symbol]: never;\n}" }, - "BillingConfigSubscriptionLineItemPlan": { - "filePath": "../../node_modules/@shopify/shopify-api/lib/billing/types.d.ts", - "name": "BillingConfigSubscriptionLineItemPlan", + "BillingContext": { + "filePath": "src/server/authenticate/admin/billing/types.ts", + "name": "BillingContext", "description": "", "members": [ { - "filePath": "../../node_modules/@shopify/shopify-api/lib/billing/types.d.ts", + "filePath": "src/server/authenticate/admin/billing/types.ts", "syntaxKind": "PropertySignature", - "name": "replacementBehavior", - "value": "BillingReplacementBehavior", - "description": "The replacement behavior to use for this plan.", - "isOptional": true + "name": "require", + "value": "(options: RequireBillingOptions) => Promise", + "description": "Checks if the shop has an active payment for any plan defined in the `billing` config option.", + "examples": [ + { + "title": "Requesting billing right away", + "description": "Call `billing.request` in the `onFailure` callback to immediately redirect to the Shopify page to request payment.", + "tabs": [ + { + "code": "import { LoaderFunctionArgs } from \"@remix-run/node\";\nimport { authenticate, MONTHLY_PLAN } from \"../shopify.server\";\n\nexport const loader = async ({ request }: LoaderFunctionArgs) => {\n const { billing } = await authenticate.admin(request);\n await billing.require({\n plans: [MONTHLY_PLAN],\n isTest: true,\n onFailure: async () => billing.request({ plan: MONTHLY_PLAN }),\n });\n\n // App logic\n};", + "title": "/app/routes/**\\/*.ts" + }, + { + "code": "import { shopifyApp, BillingInterval } from \"@shopify/shopify-app-remix/server\";\n\nexport const MONTHLY_PLAN = 'Monthly subscription';\nexport const ANNUAL_PLAN = 'Annual subscription';\n\nconst shopify = shopifyApp({\n // ...etc\n billing: {\n [MONTHLY_PLAN]: {\n amount: 5,\n currencyCode: 'USD',\n interval: BillingInterval.Every30Days,\n },\n [ANNUAL_PLAN]: {\n amount: 50,\n currencyCode: 'USD',\n interval: BillingInterval.Annual,\n },\n }\n});\nexport default shopify;\nexport const authenticate = shopify.authenticate;", + "title": "shopify.server.ts" + } + ] + }, + { + "title": "Redirect to a plan selection page", + "description": "When the app has multiple plans, create a page in your App that allows the merchant to select a plan. If a merchant does not have the required plan you can redirect them to page in your app to select one.", + "tabs": [ + { + "code": "import { LoaderFunctionArgs, redirect } from \"@remix-run/node\";\nimport { authenticate, MONTHLY_PLAN, ANNUAL_PLAN } from \"../shopify.server\";\n\nexport const loader = async ({ request }: LoaderFunctionArgs) => {\n const { billing } = await authenticate.admin(request);\n const billingCheck = await billing.require({\n plans: [MONTHLY_PLAN, ANNUAL_PLAN],\n isTest: true,\n onFailure: () => redirect('/select-plan'),\n });\n\n const subscription = billingCheck.appSubscriptions[0];\n console.log(`Shop is on ${subscription.name} (id ${subscription.id})`);\n\n // App logic\n};", + "title": "/app/routes/**\\/*.ts" + }, + { + "code": "import { shopifyApp, BillingInterval } from \"@shopify/shopify-app-remix/server\";\n\nexport const MONTHLY_PLAN = 'Monthly subscription';\nexport const ANNUAL_PLAN = 'Annual subscription';\n\nconst shopify = shopifyApp({\n // ...etc\n billing: {\n [MONTHLY_PLAN]: {\n amount: 5,\n currencyCode: 'USD',\n interval: BillingInterval.Every30Days,\n },\n [ANNUAL_PLAN]: {\n amount: 50,\n currencyCode: 'USD',\n interval: BillingInterval.Annual,\n },\n }\n});\nexport default shopify;\nexport const authenticate = shopify.authenticate;", + "title": "shopify.server.ts" + } + ] + }, + { + "title": "Requesting billing with line items", + "description": "Call `billing.request` with the `v3_lineItemBilling` future flag enabled", + "tabs": [ + { + "code": "import { LoaderFunctionArgs } from \"@remix-run/node\";\nimport { authenticate, MONTHLY_PLAN } from \"../shopify.server\";\n\nexport const loader = async ({ request }: LoaderFunctionArgs) => {\n const { billing } = await authenticate.admin(request);\n await billing.require({\n plans: [MONTHLY_PLAN],\n isTest: true,\n onFailure: async () => billing.request({ plan: MONTHLY_PLAN }),\n });\n\n // App logic\n};", + "title": "/app/routes/**\\/*.ts" + }, + { + "code": "import { shopifyApp, BillingInterval } from \"@shopify/shopify-app-remix/server\";\n\nexport const MONTHLY_PLAN = 'Monthly subscription';\nexport const ANNUAL_PLAN = 'Annual subscription';\n\nconst shopify = shopifyApp({\n // ...etc\n billing: {\n [MONTHLY_PLAN]: {\n lineItems: [\n {\n amount: 5,\n currencyCode: 'USD',\n interval: BillingInterval.Every30Days,\n },\n {\n amount: 1,\n currencyCode: 'USD',\n interval: BillingInterval.Usage.\n terms: '1 dollar per 1000 emails',\n },\n ],\n },\n }\n future: {v3_lineItemBilling: true}\n});\nexport default shopify;\nexport const authenticate = shopify.authenticate;", + "title": "shopify.server.ts" + } + ] + } + ] }, { - "filePath": "../../node_modules/@shopify/shopify-api/lib/billing/types.d.ts", + "filePath": "src/server/authenticate/admin/billing/types.ts", "syntaxKind": "PropertySignature", - "name": "trialDays", - "value": "number", - "description": "How many trial days to give before charging for this plan.", - "isOptional": true + "name": "check", + "value": "(options: CheckBillingOptions) => Promise", + "description": "Checks if the shop has an active payment for any plan defined in the `billing` config option.", + "examples": [ + { + "title": "Check what billing plans a merchant is subscribed to", + "description": "Use billing.check if you want to determine which plans are in use. Unlike `require`, `check` does notthrow an error if no active billing plans are present.", + "tabs": [ + { + "code": "import { LoaderFunctionArgs } from \"@remix-run/node\";\nimport { authenticate, MONTHLY_PLAN } from \"../shopify.server\";\n\nexport const loader = async ({ request }: LoaderFunctionArgs) => {\n const { billing } = await authenticate.admin(request);\n const { hasActivePayment, appSubscriptions } = await billing.check({\n plans: [MONTHLY_PLAN],\n isTest: false,\n });\n console.log(hasActivePayment)\n console.log(appSubscriptions)\n};", + "title": "/app/routes/**\\/*.ts" + }, + { + "code": "import { shopifyApp, BillingInterval } from \"@shopify/shopify-app-remix/server\";\n\nexport const MONTHLY_PLAN = 'Monthly subscription';\nexport const ANNUAL_PLAN = 'Annual subscription';\n\nconst shopify = shopifyApp({\n // ...etc\n billing: {\n [MONTHLY_PLAN]: {\n amount: 5,\n currencyCode: 'USD',\n interval: BillingInterval.Every30Days,\n },\n [ANNUAL_PLAN]: {\n amount: 50,\n currencyCode: 'USD',\n interval: BillingInterval.Annual,\n },\n }\n});\nexport default shopify;\nexport const authenticate = shopify.authenticate;", + "title": "shopify.server.ts" + } + ] + } + ] }, { - "filePath": "../../node_modules/@shopify/shopify-api/lib/billing/types.d.ts", + "filePath": "src/server/authenticate/admin/billing/types.ts", "syntaxKind": "PropertySignature", - "name": "lineItems", - "value": "(BillingConfigRecurringLineItem | BillingConfigUsageLineItem)[]", - "description": "The line items for this plan." - } - ], - "value": "export interface BillingConfigSubscriptionLineItemPlan {\n /**\n * The replacement behavior to use for this plan.\n */\n replacementBehavior?: BillingReplacementBehavior;\n /**\n * How many trial days to give before charging for this plan.\n */\n trialDays?: number;\n /**\n * The line items for this plan.\n */\n lineItems: (BillingConfigRecurringLineItem | BillingConfigUsageLineItem)[];\n}" - }, - "BillingReplacementBehavior": { - "filePath": "../../node_modules/@shopify/shopify-api/lib/types.d.ts", - "syntaxKind": "EnumDeclaration", - "name": "BillingReplacementBehavior", - "value": "export declare enum BillingReplacementBehavior {\n ApplyImmediately = \"APPLY_IMMEDIATELY\",\n ApplyOnNextBillingCycle = \"APPLY_ON_NEXT_BILLING_CYCLE\",\n Standard = \"STANDARD\"\n}", - "members": [ - { - "filePath": "../../node_modules/@shopify/shopify-api/lib/types.d.ts", - "name": "ApplyImmediately", - "value": "APPLY_IMMEDIATELY" - }, - { - "filePath": "../../node_modules/@shopify/shopify-api/lib/types.d.ts", - "name": "ApplyOnNextBillingCycle", - "value": "APPLY_ON_NEXT_BILLING_CYCLE" + "name": "request", + "value": "(options: RequestBillingOptions) => Promise", + "description": "Requests payment for the plan.", + "examples": [ + { + "title": "Using a custom return URL", + "description": "Change where the merchant is returned to after approving the purchase using the `returnUrl` option.", + "tabs": [ + { + "code": "import { LoaderFunctionArgs } from \"@remix-run/node\";\nimport { authenticate, MONTHLY_PLAN } from \"../shopify.server\";\n\nexport const loader = async ({ request }: LoaderFunctionArgs) => {\n const { billing } = await authenticate.admin(request);\n await billing.require({\n plans: [MONTHLY_PLAN],\n onFailure: async () => billing.request({\n plan: MONTHLY_PLAN,\n isTest: true,\n returnUrl: 'https://admin.shopify.com/store/my-store/apps/my-app/billing-page',\n }),\n });\n\n // App logic\n};", + "title": "/app/routes/**\\/*.ts" + }, + { + "code": "import { shopifyApp, BillingInterval } from \"@shopify/shopify-app-remix/server\";\n\nexport const MONTHLY_PLAN = 'Monthly subscription';\nexport const ANNUAL_PLAN = 'Annual subscription';\n\nconst shopify = shopifyApp({\n // ...etc\n billing: {\n [MONTHLY_PLAN]: {\n amount: 5,\n currencyCode: 'USD',\n interval: BillingInterval.Every30Days,\n },\n [ANNUAL_PLAN]: {\n amount: 50,\n currencyCode: 'USD',\n interval: BillingInterval.Annual,\n },\n }\n});\nexport default shopify;\nexport const authenticate = shopify.authenticate;", + "title": "shopify.server.ts" + } + ] + } + ] }, { - "filePath": "../../node_modules/@shopify/shopify-api/lib/types.d.ts", - "name": "Standard", - "value": "STANDARD" + "filePath": "src/server/authenticate/admin/billing/types.ts", + "syntaxKind": "PropertySignature", + "name": "cancel", + "value": "(options: CancelBillingOptions) => Promise", + "description": "Cancels an ongoing subscription, given its ID.", + "examples": [ + { + "title": "Cancelling a subscription", + "description": "Use the `billing.cancel` function to cancel an active subscription with the id returned from `billing.require`.", + "tabs": [ + { + "code": "import { LoaderFunctionArgs } from \"@remix-run/node\";\nimport { authenticate, MONTHLY_PLAN } from \"../shopify.server\";\n\nexport const loader = async ({ request }: LoaderFunctionArgs) => {\n const { billing } = await authenticate.admin(request);\n const billingCheck = await billing.require({\n plans: [MONTHLY_PLAN],\n onFailure: async () => billing.request({ plan: MONTHLY_PLAN }),\n });\n\n const subscription = billingCheck.appSubscriptions[0];\n const cancelledSubscription = await billing.cancel({\n subscriptionId: subscription.id,\n isTest: true,\n prorate: true,\n });\n\n // App logic\n};", + "title": "/app/routes/cancel-subscription.ts" + }, + { + "code": "import { shopifyApp, BillingInterval } from \"@shopify/shopify-app-remix/server\";\n\nexport const MONTHLY_PLAN = 'Monthly subscription';\nexport const ANNUAL_PLAN = 'Annual subscription';\n\nconst shopify = shopifyApp({\n // ...etc\n billing: {\n [MONTHLY_PLAN]: {\n amount: 5,\n currencyCode: 'USD',\n interval: BillingInterval.Every30Days,\n },\n [ANNUAL_PLAN]: {\n amount: 50,\n currencyCode: 'USD',\n interval: BillingInterval.Annual,\n },\n }\n});\nexport default shopify;\nexport const authenticate = shopify.authenticate;", + "title": "shopify.server.ts" + } + ] + } + ] } - ] + ], + "value": "export interface BillingContext {\n /**\n * Checks if the shop has an active payment for any plan defined in the `billing` config option.\n *\n * @returns A promise that resolves to an object containing the active purchases for the shop.\n *\n * @example\n * Requesting billing right away.\n * Call `billing.request` in the `onFailure` callback to immediately redirect to the Shopify page to request payment.\n * ```ts\n * // /app/routes/**\\/*.ts\n * import { LoaderFunctionArgs } from \"@remix-run/node\";\n * import { authenticate, MONTHLY_PLAN } from \"../shopify.server\";\n *\n * export const loader = async ({ request }: LoaderFunctionArgs) => {\n * const { billing } = await authenticate.admin(request);\n * await billing.require({\n * plans: [MONTHLY_PLAN],\n * isTest: true,\n * onFailure: async () => billing.request({ plan: MONTHLY_PLAN }),\n * });\n *\n * // App logic\n * };\n * ```\n * ```ts\n * // shopify.server.ts\n * import { shopifyApp, BillingInterval } from \"@shopify/shopify-app-remix/server\";\n *\n * export const MONTHLY_PLAN = 'Monthly subscription';\n * export const ANNUAL_PLAN = 'Annual subscription';\n *\n * const shopify = shopifyApp({\n * // ...etc\n * billing: {\n * [MONTHLY_PLAN]: {\n * amount: 5,\n * currencyCode: 'USD',\n * interval: BillingInterval.Every30Days,\n * },\n * [ANNUAL_PLAN]: {\n * amount: 50,\n * currencyCode: 'USD',\n * interval: BillingInterval.Annual,\n * },\n * }\n * });\n * export default shopify;\n * export const authenticate = shopify.authenticate;\n * ```\n *\n * @example\n * Redirect to a plan selection page.\n * When the app has multiple plans, create a page in your App that allows the merchant to select a plan. If a merchant does not have the required plan you can redirect them to page in your app to select one.\n * ```ts\n * // /app/routes/**\\/*.ts\n * import { LoaderFunctionArgs, redirect } from \"@remix-run/node\";\n * import { authenticate, MONTHLY_PLAN, ANNUAL_PLAN } from \"../shopify.server\";\n *\n * export const loader = async ({ request }: LoaderFunctionArgs) => {\n * const { billing } = await authenticate.admin(request);\n * const billingCheck = await billing.require({\n * plans: [MONTHLY_PLAN, ANNUAL_PLAN],\n * isTest: true,\n * onFailure: () => redirect('/select-plan'),\n * });\n *\n * const subscription = billingCheck.appSubscriptions[0];\n * console.log(`Shop is on ${subscription.name} (id ${subscription.id})`);\n *\n * // App logic\n * };\n * ```\n * ```ts\n * // shopify.server.ts\n * import { shopifyApp, BillingInterval } from \"@shopify/shopify-app-remix/server\";\n *\n * export const MONTHLY_PLAN = 'Monthly subscription';\n * export const ANNUAL_PLAN = 'Annual subscription';\n *\n * const shopify = shopifyApp({\n * // ...etc\n * billing: {\n * [MONTHLY_PLAN]: {\n * amount: 5,\n * currencyCode: 'USD',\n * interval: BillingInterval.Every30Days,\n * },\n * [ANNUAL_PLAN]: {\n * amount: 50,\n * currencyCode: 'USD',\n * interval: BillingInterval.Annual,\n * },\n * }\n * });\n * export default shopify;\n * export const authenticate = shopify.authenticate;\n * ```\n * @example\n * Requesting billing with line items\n * Call `billing.request` with the `v3_lineItemBilling` future flag enabled\n * ```ts\n * // /app/routes/**\\/*.ts\n * import { LoaderFunctionArgs } from \"@remix-run/node\";\n * import { authenticate, MONTHLY_PLAN } from \"../shopify.server\";\n *\n * export const loader = async ({ request }: LoaderFunctionArgs) => {\n * const { billing } = await authenticate.admin(request);\n * await billing.require({\n * plans: [MONTHLY_PLAN],\n * isTest: true,\n * onFailure: async () => billing.request({ plan: MONTHLY_PLAN }),\n * });\n *\n * // App logic\n * };\n * ```\n * ```ts\n * // shopify.server.ts\n * import { shopifyApp, BillingInterval } from \"@shopify/shopify-app-remix/server\";\n *\n * export const MONTHLY_PLAN = 'Monthly subscription';\n * export const ANNUAL_PLAN = 'Annual subscription';\n *\n * const shopify = shopifyApp({\n * // ...etc\n * billing: {\n * [MONTHLY_PLAN]: {\n * lineItems: [\n * {\n * amount: 5,\n * currencyCode: 'USD',\n * interval: BillingInterval.Every30Days,\n * },\n * {\n * amount: 1,\n * currencyCode: 'USD',\n * interval: BillingInterval.Usage.\n * terms: '1 dollar per 1000 emails',\n * },\n * ],\n * },\n * }\n * future: {v3_lineItemBilling: true}\n * });\n * export default shopify;\n * export const authenticate = shopify.authenticate;\n * ```\n */\n require: (\n options: RequireBillingOptions,\n ) => Promise;\n\n /**\n * Checks if the shop has an active payment for any plan defined in the `billing` config option.\n *\n * @returns A promise that resolves to an object containing the active purchases for the shop.\n *\n * @example\n * Check what billing plans a merchant is subscribed to.\n * Use billing.check if you want to determine which plans are in use. Unlike `require`, `check` does not\n * throw an error if no active billing plans are present. \n * ```ts\n * // /app/routes/**\\/*.ts\n * import { LoaderFunctionArgs } from \"@remix-run/node\";\n * import { authenticate, MONTHLY_PLAN } from \"../shopify.server\";\n *\n * export const loader = async ({ request }: LoaderFunctionArgs) => {\n * const { billing } = await authenticate.admin(request);\n * const { hasActivePayment, appSubscriptions } = await billing.check({\n * plans: [MONTHLY_PLAN],\n * isTest: false,\n * });\n * console.log(hasActivePayment)\n * console.log(appSubscriptions)\n * };\n * ```\n * ```ts\n * // shopify.server.ts\n * import { shopifyApp, BillingInterval } from \"@shopify/shopify-app-remix/server\";\n *\n * export const MONTHLY_PLAN = 'Monthly subscription';\n * export const ANNUAL_PLAN = 'Annual subscription';\n *\n * const shopify = shopifyApp({\n * // ...etc\n * billing: {\n * [MONTHLY_PLAN]: {\n * amount: 5,\n * currencyCode: 'USD',\n * interval: BillingInterval.Every30Days,\n * },\n * [ANNUAL_PLAN]: {\n * amount: 50,\n * currencyCode: 'USD',\n * interval: BillingInterval.Annual,\n * },\n * }\n * });\n * export default shopify;\n * export const authenticate = shopify.authenticate;\n * ```\n *\n */\n check: (\n options: CheckBillingOptions,\n ) => Promise;\n\n /**\n * Requests payment for the plan.\n *\n * @returns Redirects to the confirmation URL for the payment.\n *\n * @example\n * Using a custom return URL.\n * Change where the merchant is returned to after approving the purchase using the `returnUrl` option.\n * ```ts\n * // /app/routes/**\\/*.ts\n * import { LoaderFunctionArgs } from \"@remix-run/node\";\n * import { authenticate, MONTHLY_PLAN } from \"../shopify.server\";\n *\n * export const loader = async ({ request }: LoaderFunctionArgs) => {\n * const { billing } = await authenticate.admin(request);\n * await billing.require({\n * plans: [MONTHLY_PLAN],\n * onFailure: async () => billing.request({\n * plan: MONTHLY_PLAN,\n * isTest: true,\n * returnUrl: 'https://admin.shopify.com/store/my-store/apps/my-app/billing-page',\n * }),\n * });\n *\n * // App logic\n * };\n * ```\n * ```ts\n * // shopify.server.ts\n * import { shopifyApp, BillingInterval } from \"@shopify/shopify-app-remix/server\";\n *\n * export const MONTHLY_PLAN = 'Monthly subscription';\n * export const ANNUAL_PLAN = 'Annual subscription';\n *\n * const shopify = shopifyApp({\n * // ...etc\n * billing: {\n * [MONTHLY_PLAN]: {\n * amount: 5,\n * currencyCode: 'USD',\n * interval: BillingInterval.Every30Days,\n * },\n * [ANNUAL_PLAN]: {\n * amount: 50,\n * currencyCode: 'USD',\n * interval: BillingInterval.Annual,\n * },\n * }\n * });\n * export default shopify;\n * export const authenticate = shopify.authenticate;\n * ```\n */\n request: (options: RequestBillingOptions) => Promise;\n\n /**\n * Cancels an ongoing subscription, given its ID.\n *\n * @returns The cancelled subscription.\n *\n * @example\n * Cancelling a subscription.\n * Use the `billing.cancel` function to cancel an active subscription with the id returned from `billing.require`.\n * ```ts\n * // /app/routes/cancel-subscription.ts\n * import { LoaderFunctionArgs } from \"@remix-run/node\";\n * import { authenticate, MONTHLY_PLAN } from \"../shopify.server\";\n *\n * export const loader = async ({ request }: LoaderFunctionArgs) => {\n * const { billing } = await authenticate.admin(request);\n * const billingCheck = await billing.require({\n * plans: [MONTHLY_PLAN],\n * onFailure: async () => billing.request({ plan: MONTHLY_PLAN }),\n * });\n *\n * const subscription = billingCheck.appSubscriptions[0];\n * const cancelledSubscription = await billing.cancel({\n * subscriptionId: subscription.id,\n * isTest: true,\n * prorate: true,\n * });\n *\n * // App logic\n * };\n * ```\n * ```ts\n * // shopify.server.ts\n * import { shopifyApp, BillingInterval } from \"@shopify/shopify-app-remix/server\";\n *\n * export const MONTHLY_PLAN = 'Monthly subscription';\n * export const ANNUAL_PLAN = 'Annual subscription';\n *\n * const shopify = shopifyApp({\n * // ...etc\n * billing: {\n * [MONTHLY_PLAN]: {\n * amount: 5,\n * currencyCode: 'USD',\n * interval: BillingInterval.Every30Days,\n * },\n * [ANNUAL_PLAN]: {\n * amount: 50,\n * currencyCode: 'USD',\n * interval: BillingInterval.Annual,\n * },\n * }\n * });\n * export default shopify;\n * export const authenticate = shopify.authenticate;\n * ```\n */\n cancel: (options: CancelBillingOptions) => Promise;\n}" }, - "BillingConfigRecurringLineItem": { - "filePath": "../../node_modules/@shopify/shopify-api/lib/billing/types.d.ts", - "name": "BillingConfigRecurringLineItem", + "RequireBillingOptions": { + "filePath": "src/server/authenticate/admin/billing/types.ts", + "name": "RequireBillingOptions", "description": "", "members": [ { - "filePath": "../../node_modules/@shopify/shopify-api/lib/billing/types.d.ts", - "syntaxKind": "PropertySignature", - "name": "interval", - "value": "BillingInterval.Every30Days | BillingInterval.Annual", - "description": "The recurring interval for this line item.\n\nMust be either `Every30Days` or `Annual`." - }, - { - "filePath": "../../node_modules/@shopify/shopify-api/lib/billing/types.d.ts", + "filePath": "src/server/authenticate/admin/billing/types.ts", "syntaxKind": "PropertySignature", - "name": "discount", - "value": "BillingConfigSubscriptionPlanDiscount", - "description": "An optional discount to apply for this line item.", - "isOptional": true + "name": "plans", + "value": "(keyof Config[\"billing\"])[]", + "description": "The plans to check for. Must be one of the values defined in the `billing` config option." }, { - "filePath": "../../node_modules/@shopify/shopify-api/lib/billing/types.d.ts", + "filePath": "src/server/authenticate/admin/billing/types.ts", "syntaxKind": "PropertySignature", - "name": "amount", - "value": "number", - "description": "The amount to charge for this line item." + "name": "onFailure", + "value": "(error: any) => Promise", + "description": "How to handle the request if the shop doesn't have an active payment for any plan." }, { - "filePath": "../../node_modules/@shopify/shopify-api/lib/billing/types.d.ts", + "filePath": "src/server/authenticate/admin/billing/types.ts", "syntaxKind": "PropertySignature", - "name": "currencyCode", - "value": "string", - "description": "The currency code for this line item." + "name": "isTest", + "value": "boolean", + "description": "Whether to consider test purchases.", + "isOptional": true } ], - "value": "export interface BillingConfigRecurringLineItem extends BillingConfigLineItem {\n /**\n * The recurring interval for this line item.\n *\n * Must be either `Every30Days` or `Annual`.\n */\n interval: BillingInterval.Every30Days | BillingInterval.Annual;\n /**\n * An optional discount to apply for this line item.\n */\n discount?: BillingConfigSubscriptionPlanDiscount;\n}" + "value": "export interface RequireBillingOptions\n extends Omit {\n /**\n * The plans to check for. Must be one of the values defined in the `billing` config option.\n */\n plans: (keyof Config['billing'])[];\n /**\n * How to handle the request if the shop doesn't have an active payment for any plan.\n */\n onFailure: (error: any) => Promise;\n}" }, - "BillingConfigSubscriptionPlanDiscount": { + "BillingCheckResponseObject": { "filePath": "../../node_modules/@shopify/shopify-api/lib/billing/types.d.ts", - "name": "BillingConfigSubscriptionPlanDiscount", + "name": "BillingCheckResponseObject", "description": "", "members": [ { "filePath": "../../node_modules/@shopify/shopify-api/lib/billing/types.d.ts", "syntaxKind": "PropertySignature", - "name": "durationLimitInIntervals", - "value": "number", - "description": "The number of intervals to apply the discount for.", - "isOptional": true + "name": "hasActivePayment", + "value": "boolean", + "description": "Whether the user has an active payment method." }, { "filePath": "../../node_modules/@shopify/shopify-api/lib/billing/types.d.ts", "syntaxKind": "PropertySignature", - "name": "value", - "value": "BillingConfigSubscriptionPlanDiscountAmount | BillingConfigSubscriptionPlanDiscountPercentage", - "description": "The discount to apply." + "name": "oneTimePurchases", + "value": "OneTimePurchase[]", + "description": "The one-time purchases the shop has." + }, + { + "filePath": "../../node_modules/@shopify/shopify-api/lib/billing/types.d.ts", + "syntaxKind": "PropertySignature", + "name": "appSubscriptions", + "value": "AppSubscription[]", + "description": "The active subscriptions the shop has." } ], - "value": "export interface BillingConfigSubscriptionPlanDiscount {\n /**\n * The number of intervals to apply the discount for.\n */\n durationLimitInIntervals?: number;\n /**\n * The discount to apply.\n */\n value: BillingConfigSubscriptionPlanDiscountAmount | BillingConfigSubscriptionPlanDiscountPercentage;\n}" + "value": "export interface BillingCheckResponseObject {\n /**\n * Whether the user has an active payment method.\n */\n hasActivePayment: boolean;\n /**\n * The one-time purchases the shop has.\n */\n oneTimePurchases: OneTimePurchase[];\n /**\n * The active subscriptions the shop has.\n */\n appSubscriptions: AppSubscription[];\n}" }, - "BillingConfigSubscriptionPlanDiscountAmount": { + "OneTimePurchase": { "filePath": "../../node_modules/@shopify/shopify-api/lib/billing/types.d.ts", - "name": "BillingConfigSubscriptionPlanDiscountAmount", + "name": "OneTimePurchase", "description": "", "members": [ { "filePath": "../../node_modules/@shopify/shopify-api/lib/billing/types.d.ts", "syntaxKind": "PropertySignature", - "name": "amount", - "value": "number", - "description": "The amount to discount.\n\nCannot be set if `percentage` is set." + "name": "id", + "value": "string", + "description": "The ID of the one-time purchase." }, { "filePath": "../../node_modules/@shopify/shopify-api/lib/billing/types.d.ts", "syntaxKind": "PropertySignature", - "name": "percentage", - "value": "never", - "description": "The percentage to discount.\n\nCannot be set if `amount` is set.", - "isOptional": true - } - ], - "value": "export interface BillingConfigSubscriptionPlanDiscountAmount {\n /**\n * The amount to discount.\n *\n * Cannot be set if `percentage` is set.\n */\n amount: number;\n /**\n * The percentage to discount.\n *\n * Cannot be set if `amount` is set.\n */\n percentage?: never;\n}" - }, - "BillingConfigSubscriptionPlanDiscountPercentage": { - "filePath": "../../node_modules/@shopify/shopify-api/lib/billing/types.d.ts", - "name": "BillingConfigSubscriptionPlanDiscountPercentage", - "description": "", - "members": [ + "name": "name", + "value": "string", + "description": "The name of the purchased plan." + }, { "filePath": "../../node_modules/@shopify/shopify-api/lib/billing/types.d.ts", "syntaxKind": "PropertySignature", - "name": "amount", - "value": "never", - "description": "The amount to discount.\n\nCannot be set if `percentage` is set.", - "isOptional": true + "name": "test", + "value": "boolean", + "description": "Whether this is a test purchase." }, { "filePath": "../../node_modules/@shopify/shopify-api/lib/billing/types.d.ts", "syntaxKind": "PropertySignature", - "name": "percentage", - "value": "number", - "description": "The percentage to discount.\n\nCannot be set if `amount` is set." + "name": "status", + "value": "string", + "description": "The status of the one-time purchase." } ], - "value": "export interface BillingConfigSubscriptionPlanDiscountPercentage {\n /**\n * The amount to discount.\n *\n * Cannot be set if `percentage` is set.\n */\n amount?: never;\n /**\n * The percentage to discount.\n *\n * Cannot be set if `amount` is set.\n */\n percentage: number;\n}" + "value": "export interface OneTimePurchase {\n /**\n * The ID of the one-time purchase.\n */\n id: string;\n /**\n * The name of the purchased plan.\n */\n name: string;\n /**\n * Whether this is a test purchase.\n */\n test: boolean;\n /**\n * The status of the one-time purchase.\n */\n status: string;\n}" }, - "BillingConfigUsageLineItem": { + "AppSubscription": { "filePath": "../../node_modules/@shopify/shopify-api/lib/billing/types.d.ts", - "name": "BillingConfigUsageLineItem", + "name": "AppSubscription", "description": "", "members": [ { "filePath": "../../node_modules/@shopify/shopify-api/lib/billing/types.d.ts", "syntaxKind": "PropertySignature", - "name": "interval", - "value": "BillingInterval.Usage", - "description": "The usage interval for this line item.\n\nMust be set to `Usage`." - }, - { - "filePath": "../../node_modules/@shopify/shopify-api/lib/billing/types.d.ts", - "syntaxKind": "PropertySignature", - "name": "terms", + "name": "id", "value": "string", - "description": "Usage terms for this line item." + "description": "The ID of the app subscription." }, { "filePath": "../../node_modules/@shopify/shopify-api/lib/billing/types.d.ts", "syntaxKind": "PropertySignature", - "name": "amount", - "value": "number", - "description": "The amount to charge for this line item." + "name": "name", + "value": "string", + "description": "The name of the purchased plan." }, { "filePath": "../../node_modules/@shopify/shopify-api/lib/billing/types.d.ts", "syntaxKind": "PropertySignature", - "name": "currencyCode", - "value": "string", - "description": "The currency code for this line item." + "name": "test", + "value": "boolean", + "description": "Whether this is a test subscription." } ], - "value": "export interface BillingConfigUsageLineItem extends BillingConfigLineItem {\n /**\n * The usage interval for this line item.\n *\n * Must be set to `Usage`.\n */\n interval: BillingInterval.Usage;\n /**\n * Usage terms for this line item.\n */\n terms: string;\n}" - }, - "BillingConfigLegacyItem": { - "filePath": "../../node_modules/@shopify/shopify-api/lib/billing/types.d.ts", - "syntaxKind": "TypeAliasDeclaration", - "name": "BillingConfigLegacyItem", - "value": "BillingConfigOneTimePlan | BillingConfigSubscriptionPlan | BillingConfigUsagePlan", - "description": "" + "value": "export interface AppSubscription {\n /**\n * The ID of the app subscription.\n */\n id: string;\n /**\n * The name of the purchased plan.\n */\n name: string;\n /**\n * Whether this is a test subscription.\n */\n test: boolean;\n}" }, - "BillingConfigSubscriptionPlan": { - "filePath": "../../node_modules/@shopify/shopify-api/lib/billing/types.d.ts", - "name": "BillingConfigSubscriptionPlan", + "CheckBillingOptions": { + "filePath": "src/server/authenticate/admin/billing/types.ts", + "name": "CheckBillingOptions", "description": "", "members": [ { - "filePath": "../../node_modules/@shopify/shopify-api/lib/billing/types.d.ts", + "filePath": "src/server/authenticate/admin/billing/types.ts", "syntaxKind": "PropertySignature", - "name": "interval", - "value": "Exclude", - "description": "Recurring interval for this plan.\n\nMust be either `Every30Days` or `Annual`." + "name": "plans", + "value": "(keyof Config[\"billing\"])[]", + "description": "The plans to check for. Must be one of the values defined in the `billing` config option." }, { - "filePath": "../../node_modules/@shopify/shopify-api/lib/billing/types.d.ts", + "filePath": "src/server/authenticate/admin/billing/types.ts", "syntaxKind": "PropertySignature", - "name": "trialDays", - "value": "number", - "description": "How many trial days to give before charging for this plan.", + "name": "isTest", + "value": "boolean", + "description": "Whether to consider test purchases.", "isOptional": true - }, + } + ], + "value": "export interface CheckBillingOptions\n extends Omit {\n /**\n * The plans to check for. Must be one of the values defined in the `billing` config option.\n */\n plans: (keyof Config['billing'])[];\n}" + }, + "RequestBillingOptions": { + "filePath": "src/server/authenticate/admin/billing/types.ts", + "name": "RequestBillingOptions", + "description": "", + "members": [ { - "filePath": "../../node_modules/@shopify/shopify-api/lib/billing/types.d.ts", + "filePath": "src/server/authenticate/admin/billing/types.ts", "syntaxKind": "PropertySignature", - "name": "replacementBehavior", - "value": "BillingReplacementBehavior", - "description": "The behavior to use when replacing an existing subscription with a new one.", - "isOptional": true + "name": "plan", + "value": "keyof Config[\"billing\"]", + "description": "The plan to request. Must be one of the values defined in the `billing` config option." }, { - "filePath": "../../node_modules/@shopify/shopify-api/lib/billing/types.d.ts", + "filePath": "src/server/authenticate/admin/billing/types.ts", "syntaxKind": "PropertySignature", - "name": "discount", - "value": "BillingConfigSubscriptionPlanDiscount", - "description": "The discount to apply to this plan.", + "name": "isTest", + "value": "boolean", + "description": "Whether to use the test mode. This prevents the credit card from being charged. Test shops and demo shops cannot be charged.", "isOptional": true }, { - "filePath": "../../node_modules/@shopify/shopify-api/lib/billing/types.d.ts", - "syntaxKind": "PropertySignature", - "name": "amount", - "value": "number", - "description": "Amount to charge for this plan." - }, - { - "filePath": "../../node_modules/@shopify/shopify-api/lib/billing/types.d.ts", + "filePath": "src/server/authenticate/admin/billing/types.ts", "syntaxKind": "PropertySignature", - "name": "currencyCode", + "name": "returnUrl", "value": "string", - "description": "Currency code for this plan." + "description": "The URL to return to after the merchant approves the payment.", + "isOptional": true } ], - "value": "export interface BillingConfigSubscriptionPlan extends BillingConfigPlan {\n /**\n * Recurring interval for this plan.\n *\n * Must be either `Every30Days` or `Annual`.\n */\n interval: Exclude;\n /**\n * How many trial days to give before charging for this plan.\n */\n trialDays?: number;\n /**\n * The behavior to use when replacing an existing subscription with a new one.\n */\n replacementBehavior?: BillingReplacementBehavior;\n /**\n * The discount to apply to this plan.\n */\n discount?: BillingConfigSubscriptionPlanDiscount;\n}" - }, - "RecurringBillingIntervals": { - "filePath": "../../node_modules/@shopify/shopify-api/lib/types.d.ts", - "syntaxKind": "TypeAliasDeclaration", - "name": "RecurringBillingIntervals", - "value": "RecurringBillingIntervals", - "description": "" + "value": "export interface RequestBillingOptions\n extends Omit {\n /**\n * The plan to request. Must be one of the values defined in the `billing` config option.\n */\n plan: keyof Config['billing'];\n /**\n * Whether to use the test mode. This prevents the credit card from being charged. Test shops and demo shops cannot be charged.\n */\n isTest?: boolean;\n /**\n * The URL to return to after the merchant approves the payment.\n */\n returnUrl?: string;\n}" }, - "BillingConfigUsagePlan": { - "filePath": "../../node_modules/@shopify/shopify-api/lib/billing/types.d.ts", - "name": "BillingConfigUsagePlan", + "CancelBillingOptions": { + "filePath": "src/server/authenticate/admin/billing/types.ts", + "name": "CancelBillingOptions", "description": "", "members": [ { - "filePath": "../../node_modules/@shopify/shopify-api/lib/billing/types.d.ts", + "filePath": "src/server/authenticate/admin/billing/types.ts", "syntaxKind": "PropertySignature", - "name": "interval", - "value": "BillingInterval.Usage", - "description": "Interval for this plan.\n\nMust be set to `Usage`." + "name": "subscriptionId", + "value": "string", + "description": "The ID of the subscription to cancel." }, { - "filePath": "../../node_modules/@shopify/shopify-api/lib/billing/types.d.ts", - "syntaxKind": "PropertySignature", - "name": "usageTerms", - "value": "string", - "description": "Usage terms for this plan." - }, - { - "filePath": "../../node_modules/@shopify/shopify-api/lib/billing/types.d.ts", - "syntaxKind": "PropertySignature", - "name": "trialDays", - "value": "number", - "description": "How many trial days to give before charging for this plan.", - "isOptional": true - }, - { - "filePath": "../../node_modules/@shopify/shopify-api/lib/billing/types.d.ts", + "filePath": "src/server/authenticate/admin/billing/types.ts", "syntaxKind": "PropertySignature", - "name": "replacementBehavior", - "value": "BillingReplacementBehavior", - "description": "The behavior to use when replacing an existing subscription with a new one.", + "name": "prorate", + "value": "boolean", + "description": "Whether to prorate the cancellation.\n\n\n\n\n", "isOptional": true }, { - "filePath": "../../node_modules/@shopify/shopify-api/lib/billing/types.d.ts", - "syntaxKind": "PropertySignature", - "name": "amount", - "value": "number", - "description": "Amount to charge for this plan." - }, - { - "filePath": "../../node_modules/@shopify/shopify-api/lib/billing/types.d.ts", + "filePath": "src/server/authenticate/admin/billing/types.ts", "syntaxKind": "PropertySignature", - "name": "currencyCode", - "value": "string", - "description": "Currency code for this plan." - } - ], - "value": "export interface BillingConfigUsagePlan extends BillingConfigPlan {\n /**\n * Interval for this plan.\n *\n * Must be set to `Usage`.\n */\n interval: BillingInterval.Usage;\n /**\n * Usage terms for this plan.\n */\n usageTerms: string;\n /**\n * How many trial days to give before charging for this plan.\n */\n trialDays?: number;\n /**\n * The behavior to use when replacing an existing subscription with a new one.\n */\n replacementBehavior?: BillingReplacementBehavior;\n}" - }, - "LogFunction": { - "filePath": "../../node_modules/@shopify/shopify-api/lib/base-types.d.ts", - "name": "LogFunction", - "description": "A function used by the library to log events related to Shopify.", - "params": [ - { - "name": "severity", - "description": "", - "value": "LogSeverity", - "filePath": "../../node_modules/@shopify/shopify-api/lib/base-types.d.ts" - }, - { - "name": "msg", + "name": "isTest", + "value": "boolean", "description": "", - "value": "string", - "filePath": "../../node_modules/@shopify/shopify-api/lib/base-types.d.ts" + "isOptional": true } ], - "returns": { - "filePath": "../../node_modules/@shopify/shopify-api/lib/base-types.d.ts", - "description": "", - "name": "void", - "value": "void" - }, - "value": "export type LogFunction = (severity: LogSeverity, msg: string) => void;" - }, - "LogSeverity": { - "filePath": "../../node_modules/@shopify/shopify-api/lib/types.d.ts", - "syntaxKind": "EnumDeclaration", - "name": "LogSeverity", - "value": "export declare enum LogSeverity {\n Error = 0,\n Warning = 1,\n Info = 2,\n Debug = 3\n}", - "members": [ - { - "filePath": "../../node_modules/@shopify/shopify-api/lib/types.d.ts", - "name": "Error", - "value": 0 - }, - { - "filePath": "../../node_modules/@shopify/shopify-api/lib/types.d.ts", - "name": "Warning", - "value": 1 - }, - { - "filePath": "../../node_modules/@shopify/shopify-api/lib/types.d.ts", - "name": "Info", - "value": 2 - }, - { - "filePath": "../../node_modules/@shopify/shopify-api/lib/types.d.ts", - "name": "Debug", - "value": 3 - } - ] + "value": "export interface CancelBillingOptions {\n /**\n * The ID of the subscription to cancel.\n */\n subscriptionId: string;\n /**\n * Whether to prorate the cancellation.\n *\n * {@link https://shopify.dev/docs/apps/billing/subscriptions/cancel-recurring-charges}\n */\n prorate?: boolean;\n /*\n * Whether to use the test mode. This prevents the credit card from being charged. Test shops and demo shops cannot be charged.\n */\n isTest?: boolean;\n}" }, - "ShopifyApp": { - "filePath": "src/server/types.ts", - "syntaxKind": "TypeAliasDeclaration", - "name": "ShopifyApp", - "value": "Config['distribution'] extends AppDistribution.ShopifyAdmin\n ? AdminApp\n : Config['distribution'] extends AppDistribution.SingleMerchant\n ? SingleMerchantApp\n : Config['distribution'] extends AppDistribution.AppStore\n ? AppStoreApp\n : AppStoreApp", - "description": "An object your app can use to interact with Shopify.\n\nBy default, the app's distribution is `AppStore`." + "EnsureCORSFunction": { + "filePath": "src/server/authenticate/helpers/ensure-cors-headers.ts", + "name": "EnsureCORSFunction", + "description": "", + "members": [], + "value": "export interface EnsureCORSFunction {\n (response: Response): Response;\n}" }, - "AdminApp": { - "filePath": "src/server/types.ts", - "syntaxKind": "TypeAliasDeclaration", - "name": "AdminApp", - "value": "ShopifyAppBase", + "EmbeddedAdminContext": { + "filePath": "src/server/authenticate/admin/types.ts", + "name": "EmbeddedAdminContext", "description": "", "members": [ { - "filePath": "src/server/types.ts", + "filePath": "src/server/authenticate/admin/types.ts", "syntaxKind": "PropertySignature", - "name": "sessionStorage", - "value": "SessionStorageType", - "description": "The `SessionStorage` instance you passed in as a config option.", + "name": "sessionToken", + "value": "JwtPayload", + "description": "The decoded and validated session token for the request.\n\nReturned only if `isEmbeddedApp` is `true`.\n\n\n\n\n", "examples": [ { - "title": "Storing sessions with Prisma", - "description": "Import the `@shopify/shopify-app-session-storage-prisma` package to store sessions in your Prisma database.", + "title": "Using the decoded session token", + "description": "Get user-specific data using the `sessionToken` object.", "tabs": [ { - "code": "import { shopifyApp } from \"@shopify/shopify-app-remix/server\";\nimport { PrismaSessionStorage } from \"@shopify/shopify-app-session-storage-prisma\";\nimport prisma from \"~/db.server\";\n\nconst shopify = shopifyApp({\n sessionStorage: new PrismaSessionStorage(prisma),\n // ...etc\n})\n\n// shopify.sessionStorage is an instance of PrismaSessionStorage", - "title": "/app/shopify.server.ts" + "code": "import { LoaderFunctionArgs, json } from \"@remix-run/node\";\nimport { authenticate } from \"../shopify.server\";\nimport { getMyAppData } from \"~/db/model.server\";\n\nexport const loader = async ({ request }: LoaderFunctionArgs) => {\n const { sessionToken } = await authenticate.admin(\n request\n );\n return json(await getMyAppData({user: sessionToken.sub}));\n};", + "title": "/app/routes/**\\/*.ts" + }, + { + "code": "import { shopifyApp } from \"@shopify/shopify-app-remix/server\";\n\nconst shopify = shopifyApp({\n // ...etc\n useOnlineTokens: true,\n});\nexport default shopify;\nexport const authenticate = shopify.authenticate;", + "title": "shopify.server.ts" } ] } ] }, { - "filePath": "src/server/types.ts", + "filePath": "src/server/authenticate/admin/types.ts", "syntaxKind": "PropertySignature", - "name": "addDocumentResponseHeaders", - "value": "AddDocumentResponseHeaders", - "description": "Adds the required Content Security Policy headers for Shopify apps to the given Headers object.\n\n\n\n\n", + "name": "redirect", + "value": "RedirectFunction", + "description": "A function that redirects the user to a new page, ensuring that the appropriate parameters are set for embedded apps.\n\nReturned only if `isEmbeddedApp` is `true`.", "examples": [ { - "title": "Return headers on all requests", - "description": "Add headers to all HTML requests by calling `shopify.addDocumentResponseHeaders` in `entry.server.tsx`.", + "title": "Redirecting to an app route", + "description": "Use the `redirect` helper to safely redirect between pages.", "tabs": [ { - "code": "import { shopifyApp } from \"@shopify/shopify-app-remix/server\";\n\nconst shopify = shopifyApp({\n // ...etc\n});\nexport default shopify;\nexport const addDocumentResponseheaders = shopify.addDocumentResponseheaders;", - "title": "~/shopify.server.ts" - }, + "code": "import { LoaderFunctionArgs, json } from \"@remix-run/node\";\nimport { authenticate } from \"../shopify.server\";\n\nexport const loader = async ({ request }: LoaderFunctionArgs) => {\n const { session, redirect } = await authenticate.admin(request);\n return redirect(\"/\");\n};", + "title": "/app/routes/admin/my-route.ts" + } + ] + }, + { + "title": "Redirecting outside of Shopify admin", + "description": "Pass in a `target` option of `_top` or `_parent` to go to an external URL.", + "tabs": [ { - "code": "import { addDocumentResponseHeaders } from \"~/shopify.server\";\n\nexport default function handleRequest(\n request: Request,\n responseStatusCode: number,\n responseHeaders: Headers,\n remixContext: EntryContext\n) {\n const markup = renderToString(\n \n );\n\n responseHeaders.set(\"Content-Type\", \"text/html\");\n addDocumentResponseHeaders(request, responseHeaders);\n\n return new Response(\"\" + markup, {\n status: responseStatusCode,\n headers: responseHeaders,\n });\n}", - "title": "entry.server.tsx" + "code": "import { LoaderFunctionArgs, json } from \"@remix-run/node\";\nimport { authenticate } from \"../shopify.server\";\n\nexport const loader = async ({ request }: LoaderFunctionArgs) => {\n const { session, redirect } = await authenticate.admin(request);\n return redirect(\"/\", { target: '_parent' });\n};", + "title": "/app/routes/admin/my-route.ts" } ] } ] }, { - "filePath": "src/server/types.ts", + "filePath": "src/server/authenticate/admin/types.ts", "syntaxKind": "PropertySignature", - "name": "registerWebhooks", - "value": "RegisterWebhooks", - "description": "Register webhook topics for a store using the given session. Most likely you want to use this in combination with the afterAuth hook.", + "name": "session", + "value": "Session", + "description": "The session for the user who made the request.\n\nThis comes from the session storage which `shopifyApp` uses to store sessions in your database of choice.\n\nUse this to get shop or user-specific data.", "examples": [ { - "title": "Registering webhooks after install", - "description": "Trigger the registration to create the webhook subscriptions after a merchant installs your app using the `afterAuth` hook. Learn more about [subscribing to webhooks.](/docs/api/shopify-app-remix/v1/guide-webhooks)", + "title": "Using offline sessions", + "description": "Get your app's shop-specific data using an offline session.", "tabs": [ { - "code": "import { DeliveryMethod, shopifyApp } from \"@shopify/shopify-app-remix/server\";\n\nconst shopify = shopifyApp({\n hooks: {\n afterAuth: async ({ session }) => {\n shopify.registerWebhooks({ session });\n }\n },\n webhooks: {\n APP_UNINSTALLED: {\n deliveryMethod: DeliveryMethod.Http,\n callbackUrl: \"/webhooks\",\n },\n },\n // ...etc\n});", - "title": "/app/shopify.server.ts" + "code": "import { LoaderFunctionArgs, json } from \"@remix-run/node\";\nimport { authenticate } from \"../shopify.server\";\nimport { getMyAppData } from \"~/db/model.server\";\n\nexport const loader = async ({ request }: LoaderFunctionArgs) => {\n const { session } = await authenticate.admin(request);\n return json(await getMyAppData({shop: session.shop));\n};", + "title": "/app/routes/**\\/*.ts" + }, + { + "code": "import { shopifyApp } from \"@shopify/shopify-app-remix/server\";\n\nconst shopify = shopifyApp({\n // ...etc\n});\nexport default shopify;\nexport const authenticate = shopify.authenticate;", + "title": "shopify.server.ts" } ] - } - ] - }, - { - "filePath": "src/server/types.ts", - "syntaxKind": "PropertySignature", - "name": "authenticate", - "value": "Authenticate", - "description": "Ways to authenticate requests from different surfaces across Shopify.", - "examples": [ + }, { - "title": "Authenticate Shopify requests", - "description": "Use the functions in `authenticate` to validate requests coming from Shopify.", + "title": "Using online sessions", + "description": "Get your app's user-specific data using an online session.", "tabs": [ { - "code": "import { LATEST_API_VERSION, shopifyApp } from \"@shopify/shopify-app-remix/server\";\nimport { restResources } from \"@shopify/shopify-api/rest/admin/2023-04\";\n\nconst shopify = shopifyApp({\n restResources,\n // ...etc\n});\nexport default shopify;", - "title": "/app/shopify.server.ts" + "code": "import { LoaderFunctionArgs, json } from \"@remix-run/node\";\nimport { authenticate } from \"../shopify.server\";\nimport { getMyAppData } from \"~/db/model.server\";\n\nexport const loader = async ({ request }: LoaderFunctionArgs) => {\n const { session } = await authenticate.admin(request);\n return json(await getMyAppData({user: session.onlineAccessInfo!.id}));\n};", + "title": "/app/routes/**\\/*.ts" }, { - "code": "import { LoaderFunctionArgs, json } from \"@remix-run/node\";\nimport shopify from \"../../shopify.server\";\n\nexport async function loader({ request }: LoaderFunctionArgs) {\n const {admin, session, sessionToken, billing} = shopify.authenticate.admin(request);\n\n return json(await admin.rest.resources.Product.count({ session }));\n}", - "title": "/app/routes/**\\/*.jsx" + "code": "import { shopifyApp } from \"@shopify/shopify-app-remix/server\";\n\nconst shopify = shopifyApp({\n // ...etc\n useOnlineTokens: true,\n});\nexport default shopify;\nexport const authenticate = shopify.authenticate;", + "title": "shopify.server.ts" } ] } ] }, { - "filePath": "src/server/types.ts", + "filePath": "src/server/authenticate/admin/types.ts", "syntaxKind": "PropertySignature", - "name": "unauthenticated", - "value": "Unauthenticated>", - "description": "Ways to get Contexts from requests that do not originate from Shopify.", + "name": "admin", + "value": "AdminApiContext", + "description": "Methods for interacting with the GraphQL / REST Admin APIs for the store that made the request." + }, + { + "filePath": "src/server/authenticate/admin/types.ts", + "syntaxKind": "PropertySignature", + "name": "billing", + "value": "BillingContext", + "description": "Billing methods for this store, based on the plans defined in the `billing` config option.\n\n\n\n\n" + }, + { + "filePath": "src/server/authenticate/admin/types.ts", + "syntaxKind": "PropertySignature", + "name": "cors", + "value": "EnsureCORSFunction", + "description": "A function that ensures the CORS headers are set correctly for the response.", "examples": [ { - "title": "Using unauthenticated contexts", - "description": "Create contexts for requests that don't come from Shopify.", + "title": "Setting CORS headers for a admin request", + "description": "Use the `cors` helper to ensure your app can respond to requests from admin extensions.", "tabs": [ { - "code": "import { LATEST_API_VERSION, shopifyApp } from \"@shopify/shopify-app-remix/server\";\nimport { restResources } from \"@shopify/shopify-api/rest/admin/2023-04\";\n\nconst shopify = shopifyApp({\n restResources,\n // ...etc\n});\nexport default shopify;", - "title": "/app/shopify.server.ts" - }, - { - "code": "import { LoaderFunctionArgs, json } from \"@remix-run/node\";\nimport { authenticateExternal } from \"~/helpers/authenticate\"\nimport shopify from \"../../shopify.server\";\n\nexport async function loader({ request }: LoaderFunctionArgs) {\n const shop = await authenticateExternal(request)\n const {admin} = await shopify.unauthenticated.admin(shop);\n\n return json(await admin.rest.resources.Product.count({ session }));\n}", - "title": "/app/routes/**\\/*.jsx" + "code": "import { LoaderFunctionArgs, json } from \"@remix-run/node\";\nimport { authenticate } from \"../shopify.server\";\nimport { getMyAppData } from \"~/db/model.server\";\n\nexport const loader = async ({ request }: LoaderFunctionArgs) => {\n const { session, cors } = await authenticate.admin(request);\n return cors(json(await getMyAppData({user: session.onlineAccessInfo!.id})));\n};", + "title": "/app/routes/admin/my-route.ts" } ] } ] } - ] - }, - "SessionStorageType": { - "filePath": "src/server/types.ts", - "syntaxKind": "TypeAliasDeclaration", - "name": "SessionStorageType", - "value": "Config['sessionStorage'] extends SessionStorage\n ? Config['sessionStorage']\n : SessionStorage", - "description": "" + ], + "value": "export interface EmbeddedAdminContext<\n Config extends AppConfigArg,\n Resources extends ShopifyRestResources = ShopifyRestResources,\n> extends AdminContextInternal {\n /**\n * The decoded and validated session token for the request.\n *\n * Returned only if `isEmbeddedApp` is `true`.\n *\n * {@link https://shopify.dev/docs/apps/auth/oauth/session-tokens#payload}\n *\n * @example\n * Using the decoded session token.\n * Get user-specific data using the `sessionToken` object.\n * ```ts\n * // /app/routes/**\\/*.ts\n * import { LoaderFunctionArgs, json } from \"@remix-run/node\";\n * import { authenticate } from \"../shopify.server\";\n * import { getMyAppData } from \"~/db/model.server\";\n *\n * export const loader = async ({ request }: LoaderFunctionArgs) => {\n * const { sessionToken } = await authenticate.admin(\n * request\n * );\n * return json(await getMyAppData({user: sessionToken.sub}));\n * };\n * ```\n * ```ts\n * // shopify.server.ts\n * import { shopifyApp } from \"@shopify/shopify-app-remix/server\";\n *\n * const shopify = shopifyApp({\n * // ...etc\n * useOnlineTokens: true,\n * });\n * export default shopify;\n * export const authenticate = shopify.authenticate;\n * ```\n */\n sessionToken: JwtPayload;\n\n /**\n * A function that redirects the user to a new page, ensuring that the appropriate parameters are set for embedded\n * apps.\n *\n * Returned only if `isEmbeddedApp` is `true`.\n *\n * @example\n * Redirecting to an app route.\n * Use the `redirect` helper to safely redirect between pages.\n * ```ts\n * // /app/routes/admin/my-route.ts\n * import { LoaderFunctionArgs, json } from \"@remix-run/node\";\n * import { authenticate } from \"../shopify.server\";\n *\n * export const loader = async ({ request }: LoaderFunctionArgs) => {\n * const { session, redirect } = await authenticate.admin(request);\n * return redirect(\"/\");\n * };\n * ```\n *\n * @example\n * Redirecting outside of Shopify admin.\n * Pass in a `target` option of `_top` or `_parent` to go to an external URL.\n * ```ts\n * // /app/routes/admin/my-route.ts\n * import { LoaderFunctionArgs, json } from \"@remix-run/node\";\n * import { authenticate } from \"../shopify.server\";\n *\n * export const loader = async ({ request }: LoaderFunctionArgs) => {\n * const { session, redirect } = await authenticate.admin(request);\n * return redirect(\"/\", { target: '_parent' });\n * };\n * ```\n */\n redirect: RedirectFunction;\n}" }, - "AddDocumentResponseHeaders": { - "filePath": "src/server/types.ts", - "name": "AddDocumentResponseHeaders", + "JwtPayload": { + "filePath": "../../node_modules/@shopify/shopify-api/lib/session/types.d.ts", + "name": "JwtPayload", "description": "", - "params": [ + "members": [ { - "name": "request", - "description": "", - "value": "Request", - "filePath": "src/server/types.ts" + "filePath": "../../node_modules/@shopify/shopify-api/lib/session/types.d.ts", + "syntaxKind": "PropertySignature", + "name": "iss", + "value": "string", + "description": "The shop's admin domain." }, { - "name": "headers", + "filePath": "../../node_modules/@shopify/shopify-api/lib/session/types.d.ts", + "syntaxKind": "PropertySignature", + "name": "dest", + "value": "string", + "description": "The shop's domain." + }, + { + "filePath": "../../node_modules/@shopify/shopify-api/lib/session/types.d.ts", + "syntaxKind": "PropertySignature", + "name": "aud", + "value": "string", + "description": "The client ID of the receiving app." + }, + { + "filePath": "../../node_modules/@shopify/shopify-api/lib/session/types.d.ts", + "syntaxKind": "PropertySignature", + "name": "sub", + "value": "string", + "description": "The User that the session token is intended for." + }, + { + "filePath": "../../node_modules/@shopify/shopify-api/lib/session/types.d.ts", + "syntaxKind": "PropertySignature", + "name": "exp", + "value": "number", + "description": "When the session token expires." + }, + { + "filePath": "../../node_modules/@shopify/shopify-api/lib/session/types.d.ts", + "syntaxKind": "PropertySignature", + "name": "nbf", + "value": "number", + "description": "When the session token activates." + }, + { + "filePath": "../../node_modules/@shopify/shopify-api/lib/session/types.d.ts", + "syntaxKind": "PropertySignature", + "name": "iat", + "value": "number", + "description": "When the session token was issued." + }, + { + "filePath": "../../node_modules/@shopify/shopify-api/lib/session/types.d.ts", + "syntaxKind": "PropertySignature", + "name": "jti", + "value": "string", + "description": "A secure random UUID." + }, + { + "filePath": "../../node_modules/@shopify/shopify-api/lib/session/types.d.ts", + "syntaxKind": "PropertySignature", + "name": "sid", + "value": "string", + "description": "A unique session ID per user and app." + } + ], + "value": "export interface JwtPayload {\n /**\n * The shop's admin domain.\n */\n iss: string;\n /**\n * The shop's domain.\n */\n dest: string;\n /**\n * The client ID of the receiving app.\n */\n aud: string;\n /**\n * The User that the session token is intended for.\n */\n sub: string;\n /**\n * When the session token expires.\n */\n exp: number;\n /**\n * When the session token activates.\n */\n nbf: number;\n /**\n * When the session token was issued.\n */\n iat: number;\n /**\n * A secure random UUID.\n */\n jti: string;\n /**\n * A unique session ID per user and app.\n */\n sid: string;\n}" + }, + "RedirectFunction": { + "filePath": "src/server/authenticate/admin/helpers/redirect.ts", + "name": "RedirectFunction", + "description": "", + "params": [ + { + "name": "url", "description": "", - "value": "Headers", - "filePath": "src/server/types.ts" + "value": "string", + "filePath": "src/server/authenticate/admin/helpers/redirect.ts" + }, + { + "name": "init", + "description": "", + "value": "RedirectInit", + "isOptional": true, + "filePath": "src/server/authenticate/admin/helpers/redirect.ts" } ], "returns": { - "filePath": "src/server/types.ts", + "filePath": "src/server/authenticate/admin/helpers/redirect.ts", "description": "", - "name": "void", - "value": "void" + "name": "TypedResponse", + "value": "TypedResponse" }, - "value": "type AddDocumentResponseHeaders = (request: Request, headers: Headers) => void;" + "value": "export type RedirectFunction = (\n url: string,\n init?: RedirectInit,\n) => TypedResponse;" }, - "RegisterWebhooks": { + "RedirectInit": { + "filePath": "src/server/authenticate/admin/helpers/redirect.ts", + "syntaxKind": "TypeAliasDeclaration", + "name": "RedirectInit", + "value": "number | (ResponseInit & {target?: RedirectTarget})", + "description": "" + }, + "RedirectTarget": { + "filePath": "src/server/authenticate/admin/helpers/redirect.ts", + "syntaxKind": "TypeAliasDeclaration", + "name": "RedirectTarget", + "value": "'_self' | '_parent' | '_top'", + "description": "" + }, + "RestResourcesType": { "filePath": "src/server/types.ts", - "name": "RegisterWebhooks", + "syntaxKind": "TypeAliasDeclaration", + "name": "RestResourcesType", + "value": "Config['restResources'] extends ShopifyRestResources\n ? Config['restResources']\n : ShopifyRestResources", + "description": "" + }, + "ShopifyRestResources": { + "filePath": "../../node_modules/@shopify/shopify-api/rest/types.d.ts", + "syntaxKind": "TypeAliasDeclaration", + "name": "ShopifyRestResources", + "value": "Record", + "description": "", + "members": [] + }, + "AuthenticateFlow": { + "filePath": "src/server/authenticate/flow/types.ts", + "name": "AuthenticateFlow", "description": "", "params": [ { - "name": "options", + "name": "request", "description": "", - "value": "RegisterWebhooksOptions", - "filePath": "src/server/types.ts" + "value": "Request", + "filePath": "src/server/authenticate/flow/types.ts" } ], "returns": { - "filePath": "src/server/types.ts", + "filePath": "src/server/authenticate/flow/types.ts", "description": "", - "name": "Promise", - "value": "Promise" + "name": "Promise>", + "value": "Promise>" }, - "value": "type RegisterWebhooks = (\n options: RegisterWebhooksOptions,\n) => Promise;" + "value": "export type AuthenticateFlow<\n Resources extends ShopifyRestResources = ShopifyRestResources,\n> = (request: Request) => Promise>;" }, - "RegisterWebhooksOptions": { - "filePath": "src/server/authenticate/webhooks/types.ts", - "name": "RegisterWebhooksOptions", + "FlowContext": { + "filePath": "src/server/authenticate/flow/types.ts", + "name": "FlowContext", "description": "", "members": [ { - "filePath": "src/server/authenticate/webhooks/types.ts", + "filePath": "src/server/authenticate/flow/types.ts", "syntaxKind": "PropertySignature", "name": "session", "value": "Session", - "description": "The Shopify session used to register webhooks using the Admin API." - } - ], - "value": "export interface RegisterWebhooksOptions {\n /**\n * The Shopify session used to register webhooks using the Admin API.\n */\n session: Session;\n}" - }, - "RegisterReturn": { - "filePath": "../../node_modules/@shopify/shopify-api/lib/webhooks/types.d.ts", - "syntaxKind": "TypeAliasDeclaration", - "name": "RegisterReturn", - "value": "Record", - "description": "", - "members": [] - }, - "Authenticate": { - "filePath": "src/server/types.ts", - "name": "Authenticate", - "description": "", - "members": [ - { - "filePath": "src/server/types.ts", - "syntaxKind": "PropertySignature", - "name": "admin", - "value": "AuthenticateAdmin>", - "description": "Authenticate an admin Request and get back an authenticated admin context. Use the authenticated admin context to interact with Shopify.\n\nExamples of when to use this are requests from your app's UI, or requests from admin extensions.\n\nIf there is no session for the Request, this will redirect the merchant to correct auth flows.", + "description": "A session with an offline token for the shop.\n\nReturned only if there is a session for the shop.", "examples": [ { - "title": "Authenticating a request for an embedded app", - "description": "", + "title": "Shopify session for the Flow request", + "description": "Use the session associated with this request to use REST resources.", "tabs": [ { - "code": "import { LoaderFunctionArgs, json } from \"@remix-run/node\";\nimport { authenticate } from \"../../shopify.server\";\n\nexport async function loader({ request }: LoaderFunctionArgs) {\n const {admin, session, sessionToken, billing} = authenticate.admin(request);\n\n return json(await admin.rest.resources.Product.count({ session }));\n}", - "title": "/app/routes/**\\/*.jsx" - }, - { - "code": "import { LATEST_API_VERSION, shopifyApp } from \"@shopify/shopify-app-remix/server\";\nimport { restResources } from \"@shopify/shopify-api/rest/admin/2023-04\";\n\nconst shopify = shopifyApp({\n restResources,\n // ...etc\n});\nexport default shopify;\nexport const authenticate = shopify.authenticate;", - "title": "/app/shopify.server.ts" + "code": "import { ActionFunctionArgs } from \"@remix-run/node\";\nimport { authenticate } from \"../shopify.server\";\n\nexport const action = async ({ request }: ActionFunctionArgs) => {\n const { session, admin } = await authenticate.flow(request);\n\n const products = await admin?.rest.resources.Product.all({ session });\n // Use products\n\n return new Response();\n};", + "title": "/app/routes/flow.tsx" } ] } ] }, { - "filePath": "src/server/types.ts", + "filePath": "src/server/authenticate/flow/types.ts", "syntaxKind": "PropertySignature", - "name": "flow", - "value": "AuthenticateFlow>", - "description": "Authenticate a Flow extension Request and get back an authenticated context, containing an admin context to access the API, and the payload of the request.\n\nIf there is no session for the Request, this will return an HTTP 400 error.\n\nNote that this will always be a POST request.", + "name": "payload", + "value": "any", + "description": "The payload from the Flow request.", "examples": [ { - "title": "Authenticating a Flow extension request", - "description": "", + "title": "Flow payload", + "description": "Get the request's POST payload.", "tabs": [ { - "code": "import { ActionFunctionArgs, json } from \"@remix-run/node\";\nimport { authenticate } from \"../../shopify.server\";\n\nexport async function action({ request }: ActionFunctionArgs) {\n const {admin, session, payload} = authenticate.flow(request);\n\n // Perform flow extension logic\n\n // Return a 200 response\n return null;\n}", - "title": "/app/routes/**\\/*.jsx" - }, - { - "code": "import { LATEST_API_VERSION, shopifyApp } from \"@shopify/shopify-app-remix/server\";\nimport { restResources } from \"@shopify/shopify-api/rest/admin/2023-04\";\n\nconst shopify = shopifyApp({\n restResources,\n // ...etc\n});\nexport default shopify;\nexport const authenticate = shopify.authenticate;", - "title": "/app/shopify.server.ts" + "code": "import { ActionFunctionArgs } from \"@remix-run/node\";\nimport { authenticate } from \"../shopify.server\";\n\nexport const action = async ({ request }: ActionFunctionArgs) => {\n const { payload } = await authenticate.flow(request);\n return new Response();\n};", + "title": "/app/routes/flow.tsx" } ] } ] }, { - "filePath": "src/server/types.ts", + "filePath": "src/server/authenticate/flow/types.ts", "syntaxKind": "PropertySignature", - "name": "public", - "value": "AuthenticatePublic", - "description": "Authenticate a public request and get back a session token.", + "name": "admin", + "value": "AdminApiContext", + "description": "An admin context for the Flow request.\n\nReturned only if there is a session for the shop.", "examples": [ { - "title": "Authenticating a request from a checkout extension", - "description": "", + "title": "Flow admin context", + "description": "Use the `admin` object in the context to interact with the Admin API.", "tabs": [ { - "code": "import { LoaderFunctionArgs, json } from \"@remix-run/node\";\nimport { authenticate } from \"../../shopify.server\";\nimport { getWidgets } from \"~/db/widgets\";\n\nexport async function loader({ request }: LoaderFunctionArgs) {\n const {sessionToken} = authenticate.public.checkout(request);\n\n return json(await getWidgets(sessionToken));\n}", - "title": "/app/routes/api/checkout.jsx" + "code": "import { ActionFunctionArgs } from \"@remix-run/node\";\nimport { authenticate } from \"../shopify.server\";\n\nexport async function action({ request }: ActionFunctionArgs) {\n const { admin } = await authenticate.flow(request);\n\n const response = await admin?.graphql(\n `#graphql\n mutation populateProduct($input: ProductInput!) {\n productCreate(input: $input) {\n product {\n id\n }\n }\n }`,\n { variables: { input: { title: \"Product Name\" } } }\n );\n\n const productData = await response.json();\n return json({ data: productData.data });\n}", + "title": "/app/routes/flow.tsx" } ] } ] - }, + } + ], + "value": "export interface FlowContext<\n Resources extends ShopifyRestResources = ShopifyRestResources,\n> {\n /**\n * A session with an offline token for the shop.\n *\n * Returned only if there is a session for the shop.\n *\n * @example\n * Shopify session for the Flow request.\n * Use the session associated with this request to use REST resources.\n * ```ts\n * // /app/routes/flow.tsx\n * import { ActionFunctionArgs } from \"@remix-run/node\";\n * import { authenticate } from \"../shopify.server\";\n *\n * export const action = async ({ request }: ActionFunctionArgs) => {\n * const { session, admin } = await authenticate.flow(request);\n *\n * const products = await admin?.rest.resources.Product.all({ session });\n * // Use products\n *\n * return new Response();\n * };\n * ```\n */\n session: Session;\n\n /**\n * The payload from the Flow request.\n *\n * @example\n * Flow payload.\n * Get the request's POST payload.\n * ```ts\n * // /app/routes/flow.tsx\n * import { ActionFunctionArgs } from \"@remix-run/node\";\n * import { authenticate } from \"../shopify.server\";\n *\n * export const action = async ({ request }: ActionFunctionArgs) => {\n * const { payload } = await authenticate.flow(request);\n * return new Response();\n * };\n * ```\n */\n payload: any;\n\n /**\n * An admin context for the Flow request.\n *\n * Returned only if there is a session for the shop.\n *\n * @example\n * Flow admin context.\n * Use the `admin` object in the context to interact with the Admin API.\n * ```ts\n * // /app/routes/flow.tsx\n * import { ActionFunctionArgs } from \"@remix-run/node\";\n * import { authenticate } from \"../shopify.server\";\n *\n * export async function action({ request }: ActionFunctionArgs) {\n * const { admin } = await authenticate.flow(request);\n *\n * const response = await admin?.graphql(\n * `#graphql\n * mutation populateProduct($input: ProductInput!) {\n * productCreate(input: $input) {\n * product {\n * id\n * }\n * }\n * }`,\n * { variables: { input: { title: \"Product Name\" } } }\n * );\n *\n * const productData = await response.json();\n * return json({ data: productData.data });\n * }\n * ```\n */\n admin: AdminApiContext;\n}" + }, + "AuthenticatePublic": { + "filePath": "src/server/authenticate/public/types.ts", + "syntaxKind": "TypeAliasDeclaration", + "name": "AuthenticatePublic", + "value": "FeatureEnabled extends true\n ? AuthenticatePublicObject\n : AuthenticatePublicLegacy", + "description": "" + }, + "AuthenticatePublicObject": { + "filePath": "src/server/authenticate/public/types.ts", + "name": "AuthenticatePublicObject", + "description": "", + "members": [ { - "filePath": "src/server/types.ts", + "filePath": "src/server/authenticate/public/types.ts", "syntaxKind": "PropertySignature", - "name": "webhook", - "value": "AuthenticateWebhook<\n Config['future'],\n RestResourcesType,\n keyof Config['webhooks'] | MandatoryTopics\n >", - "description": "Authenticate a Shopify webhook request, get back an authenticated admin context and details on the webhook request", + "name": "checkout", + "value": "AuthenticateCheckout", + "description": "Authenticate a request from a checkout extension", "examples": [ { - "title": "Authenticating a webhook request", + "title": "Authenticating a checkout extension request", "description": "", "tabs": [ { - "code": "import { DeliveryMethod, shopifyApp } from \"@shopify/shopify-app-remix/server\";\n\nconst shopify = shopifyApp({\n webhooks: {\n APP_UNINSTALLED: {\n deliveryMethod: DeliveryMethod.Http,\n callbackUrl: \"/webhooks\",\n },\n },\n hooks: {\n afterAuth: async ({ session }) => {\n shopify.registerWebhooks({ session });\n },\n },\n // ...etc\n});\nexport default shopify;\nexport const authenticate = shopify.authenticate;", - "title": "/app/shopify.server.ts" - }, - { - "code": "import { ActionFunctionArgs } from \"@remix-run/node\";\nimport { authenticate } from \"../shopify.server\";\nimport db from \"../db.server\";\n\nexport const action = async ({ request }: ActionFunctionArgs) => {\n const { topic, shop, session } = await authenticate.webhook(request);\n\n switch (topic) {\n case \"APP_UNINSTALLED\":\n if (session) {\n await db.session.deleteMany({ where: { shop } });\n }\n break;\n case \"CUSTOMERS_DATA_REQUEST\":\n case \"CUSTOMERS_REDACT\":\n case \"SHOP_REDACT\":\n default:\n throw new Response(\"Unhandled webhook topic\", { status: 404 });\n }\n\n throw new Response();\n};", - "title": "/app/routes/webhooks.ts" + "code": "import { LoaderFunctionArgs, json } from \"@remix-run/node\";\nimport { authenticate } from \"../shopify.server\";\n\nexport const loader = async ({ request }: LoaderFunctionArgs) => {\n const { sessionToken, cors } = await authenticate.public.checkout(\n request,\n );\n return cors(json({my: \"data\", shop: sessionToken.dest}));\n};", + "title": "/app/routes/public/widgets.ts" } ] } ] - } - ], - "value": "interface Authenticate {\n /**\n * Authenticate an admin Request and get back an authenticated admin context. Use the authenticated admin context to interact with Shopify.\n *\n * Examples of when to use this are requests from your app's UI, or requests from admin extensions.\n *\n * If there is no session for the Request, this will redirect the merchant to correct auth flows.\n *\n * @example\n * Authenticating a request for an embedded app.\n * ```ts\n * // /app/routes/**\\/*.jsx\n * import { LoaderFunctionArgs, json } from \"@remix-run/node\";\n * import { authenticate } from \"../../shopify.server\";\n *\n * export async function loader({ request }: LoaderFunctionArgs) {\n * const {admin, session, sessionToken, billing} = authenticate.admin(request);\n *\n * return json(await admin.rest.resources.Product.count({ session }));\n * }\n * ```\n * ```ts\n * // /app/shopify.server.ts\n * import { LATEST_API_VERSION, shopifyApp } from \"@shopify/shopify-app-remix/server\";\n * import { restResources } from \"@shopify/shopify-api/rest/admin/2023-04\";\n *\n * const shopify = shopifyApp({\n * restResources,\n * // ...etc\n * });\n * export default shopify;\n * export const authenticate = shopify.authenticate;\n * ```\n */\n admin: AuthenticateAdmin>;\n\n /**\n * Authenticate a Flow extension Request and get back an authenticated context, containing an admin context to access\n * the API, and the payload of the request.\n *\n * If there is no session for the Request, this will return an HTTP 400 error.\n *\n * Note that this will always be a POST request.\n *\n * @example\n * Authenticating a Flow extension request.\n * ```ts\n * // /app/routes/**\\/*.jsx\n * import { ActionFunctionArgs, json } from \"@remix-run/node\";\n * import { authenticate } from \"../../shopify.server\";\n *\n * export async function action({ request }: ActionFunctionArgs) {\n * const {admin, session, payload} = authenticate.flow(request);\n *\n * // Perform flow extension logic\n *\n * // Return a 200 response\n * return null;\n * }\n * ```\n * ```ts\n * // /app/shopify.server.ts\n * import { LATEST_API_VERSION, shopifyApp } from \"@shopify/shopify-app-remix/server\";\n * import { restResources } from \"@shopify/shopify-api/rest/admin/2023-04\";\n *\n * const shopify = shopifyApp({\n * restResources,\n * // ...etc\n * });\n * export default shopify;\n * export const authenticate = shopify.authenticate;\n * ```\n */\n flow: AuthenticateFlow>;\n\n /**\n * Authenticate a public request and get back a session token.\n *\n * @example\n * Authenticating a request from a checkout extension\n *\n * ```ts\n * // /app/routes/api/checkout.jsx\n * import { LoaderFunctionArgs, json } from \"@remix-run/node\";\n * import { authenticate } from \"../../shopify.server\";\n * import { getWidgets } from \"~/db/widgets\";\n *\n * export async function loader({ request }: LoaderFunctionArgs) {\n * const {sessionToken} = authenticate.public.checkout(request);\n *\n * return json(await getWidgets(sessionToken));\n * }\n * ```\n */\n public: AuthenticatePublic;\n\n /**\n * Authenticate a Shopify webhook request, get back an authenticated admin context and details on the webhook request\n *\n * @example\n * Authenticating a webhook request\n *\n * ```ts\n * // /app/shopify.server.ts\n * import { DeliveryMethod, shopifyApp } from \"@shopify/shopify-app-remix/server\";\n *\n * const shopify = shopifyApp({\n * webhooks: {\n * APP_UNINSTALLED: {\n * deliveryMethod: DeliveryMethod.Http,\n * callbackUrl: \"/webhooks\",\n * },\n * },\n * hooks: {\n * afterAuth: async ({ session }) => {\n * shopify.registerWebhooks({ session });\n * },\n * },\n * // ...etc\n * });\n * export default shopify;\n * export const authenticate = shopify.authenticate;\n * ```\n * ```ts\n * // /app/routes/webhooks.ts\n * import { ActionFunctionArgs } from \"@remix-run/node\";\n * import { authenticate } from \"../shopify.server\";\n * import db from \"../db.server\";\n *\n * export const action = async ({ request }: ActionFunctionArgs) => {\n * const { topic, shop, session } = await authenticate.webhook(request);\n *\n * switch (topic) {\n * case \"APP_UNINSTALLED\":\n * if (session) {\n * await db.session.deleteMany({ where: { shop } });\n * }\n * break;\n * case \"CUSTOMERS_DATA_REQUEST\":\n * case \"CUSTOMERS_REDACT\":\n * case \"SHOP_REDACT\":\n * default:\n * throw new Response(\"Unhandled webhook topic\", { status: 404 });\n * }\n *\n * throw new Response();\n * };\n * ```\n */\n webhook: AuthenticateWebhook<\n Config['future'],\n RestResourcesType,\n keyof Config['webhooks'] | MandatoryTopics\n >;\n}" - }, - "AuthenticateAdmin": { - "filePath": "src/server/authenticate/admin/types.ts", - "name": "AuthenticateAdmin", + }, + { + "filePath": "src/server/authenticate/public/types.ts", + "syntaxKind": "PropertySignature", + "name": "appProxy", + "value": "AuthenticateAppProxy", + "description": "Authenticate a request from an app proxy", + "examples": [ + { + "title": "Authenticating an app proxy request", + "description": "", + "tabs": [ + { + "code": "import { LoaderFunctionArgs, json } from \"@remix-run/node\";\nimport { authenticate } from \"../shopify.server\";\n\nexport const loader = async ({ request }: LoaderFunctionArgs) => {\n await authenticate.public.appProxy(\n request,\n );\n\n const {searchParams} = new URL(request.url);\n const shop = searchParams.get(\"shop\");\n const customerId = searchParams.get(\"logged_in_customer_id\")\n\n return json({my: \"data\", shop, customerId});\n};", + "title": "/app/routes/public/widgets.ts" + } + ] + } + ] + } + ], + "value": "export interface AuthenticatePublicObject {\n /**\n * Authenticate a request from a checkout extension\n *\n * @example\n * Authenticating a checkout extension request\n * ```ts\n * // /app/routes/public/widgets.ts\n * import { LoaderFunctionArgs, json } from \"@remix-run/node\";\n * import { authenticate } from \"../shopify.server\";\n *\n * export const loader = async ({ request }: LoaderFunctionArgs) => {\n * const { sessionToken, cors } = await authenticate.public.checkout(\n * request,\n * );\n * return cors(json({my: \"data\", shop: sessionToken.dest}));\n * };\n * ```\n */\n checkout: AuthenticateCheckout;\n\n /**\n * Authenticate a request from an app proxy\n *\n * @example\n * Authenticating an app proxy request\n * ```ts\n * // /app/routes/public/widgets.ts\n * import { LoaderFunctionArgs, json } from \"@remix-run/node\";\n * import { authenticate } from \"../shopify.server\";\n *\n * export const loader = async ({ request }: LoaderFunctionArgs) => {\n * await authenticate.public.appProxy(\n * request,\n * );\n *\n * const {searchParams} = new URL(request.url);\n * const shop = searchParams.get(\"shop\");\n * const customerId = searchParams.get(\"logged_in_customer_id\")\n *\n * return json({my: \"data\", shop, customerId});\n * };\n * ```\n */\n appProxy: AuthenticateAppProxy;\n}" + }, + "AuthenticateCheckout": { + "filePath": "src/server/authenticate/public/checkout/types.ts", + "name": "AuthenticateCheckout", "description": "", "params": [ { "name": "request", "description": "", "value": "Request", - "filePath": "src/server/authenticate/admin/types.ts" + "filePath": "src/server/authenticate/public/checkout/types.ts" + }, + { + "name": "options", + "description": "", + "value": "AuthenticateCheckoutOptions", + "isOptional": true, + "filePath": "src/server/authenticate/public/checkout/types.ts" } ], "returns": { - "filePath": "src/server/authenticate/admin/types.ts", + "filePath": "src/server/authenticate/public/checkout/types.ts", "description": "", - "name": "Promise>", - "value": "Promise>" + "name": "Promise", + "value": "Promise" }, - "value": "export type AuthenticateAdmin<\n Config extends AppConfigArg,\n Resources extends ShopifyRestResources = ShopifyRestResources,\n> = (request: Request) => Promise>;" - }, - "AdminContext": { - "filePath": "src/server/authenticate/admin/types.ts", - "syntaxKind": "TypeAliasDeclaration", - "name": "AdminContext", - "value": "Config['isEmbeddedApp'] extends false\n ? NonEmbeddedAdminContext\n : EmbeddedAdminContext", - "description": "" + "value": "export type AuthenticateCheckout = (\n request: Request,\n options?: AuthenticateCheckoutOptions,\n) => Promise;" }, - "NonEmbeddedAdminContext": { - "filePath": "src/server/authenticate/admin/types.ts", - "name": "NonEmbeddedAdminContext", + "AuthenticateCheckoutOptions": { + "filePath": "src/server/authenticate/public/checkout/types.ts", + "name": "AuthenticateCheckoutOptions", "description": "", "members": [ { - "filePath": "src/server/authenticate/admin/types.ts", + "filePath": "src/server/authenticate/public/checkout/types.ts", "syntaxKind": "PropertySignature", - "name": "session", - "value": "Session", - "description": "The session for the user who made the request.\n\nThis comes from the session storage which `shopifyApp` uses to store sessions in your database of choice.\n\nUse this to get shop or user-specific data.", + "name": "corsHeaders", + "value": "string[]", + "description": "", + "isOptional": true + } + ], + "value": "export interface AuthenticateCheckoutOptions {\n corsHeaders?: string[];\n}" + }, + "CheckoutContext": { + "filePath": "src/server/authenticate/public/checkout/types.ts", + "name": "CheckoutContext", + "description": "Authenticated Context for a checkout request", + "members": [ + { + "filePath": "src/server/authenticate/public/checkout/types.ts", + "syntaxKind": "PropertySignature", + "name": "sessionToken", + "value": "JwtPayload", + "description": "The decoded and validated session token for the request\n\nRefer to the OAuth docs for the [session token payload](https://shopify.dev/docs/apps/auth/oauth/session-tokens#payload).", "examples": [ { - "title": "Using offline sessions", - "description": "Get your app's shop-specific data using an offline session.", - "tabs": [ - { - "code": "import { shopifyApp } from \"@shopify/shopify-app-remix/server\";\n\nconst shopify = shopifyApp({\n // ...etc\n});\nexport default shopify;\nexport const authenticate = shopify.authenticate;", - "title": "shopify.server.ts" - }, - { - "code": "import { LoaderFunctionArgs, json } from \"@remix-run/node\";\nimport { authenticate } from \"../shopify.server\";\nimport { getMyAppData } from \"~/db/model.server\";\n\nexport const loader = async ({ request }: LoaderFunctionArgs) => {\n const { session } = await authenticate.admin(request);\n return json(await getMyAppData({shop: session.shop));\n};", - "title": "/app/routes/**\\/*.ts" - } - ] - }, - { - "title": "Using online sessions", - "description": "Get your app's user-specific data using an online session.", + "title": "Using the decoded session token", + "description": "Get store-specific data using the `sessionToken` object.", "tabs": [ { - "code": "import { shopifyApp } from \"@shopify/shopify-app-remix/server\";\n\nconst shopify = shopifyApp({\n // ...etc\n useOnlineTokens: true,\n});\nexport default shopify;\nexport const authenticate = shopify.authenticate;", - "title": "shopify.server.ts" - }, - { - "code": "import { LoaderFunctionArgs, json } from \"@remix-run/node\";\nimport { authenticate } from \"../shopify.server\";\nimport { getMyAppData } from \"~/db/model.server\";\n\nexport const loader = async ({ request }: LoaderFunctionArgs) => {\n const { session } = await authenticate.admin(request);\n return json(await getMyAppData({user: session.onlineAccessInfo!.id}));\n};", - "title": "/app/routes/**\\/*.ts" + "code": "import { LoaderFunctionArgs, json } from \"@remix-run/node\";\nimport { authenticate } from \"../shopify.server\";\nimport { getMyAppData } from \"~/db/model.server\";\n\nexport const loader = async ({ request }: LoaderFunctionArgs) => {\n const { sessionToken } = await authenticate.public.checkout(\n request\n );\n return json(await getMyAppData({shop: sessionToken.dest}));\n};", + "title": "app/routes/public/my-route.ts" } ] } ] }, { - "filePath": "src/server/authenticate/admin/types.ts", - "syntaxKind": "PropertySignature", - "name": "admin", - "value": "AdminApiContext", - "description": "Methods for interacting with the GraphQL / REST Admin APIs for the store that made the request." - }, - { - "filePath": "src/server/authenticate/admin/types.ts", - "syntaxKind": "PropertySignature", - "name": "billing", - "value": "BillingContext", - "description": "Billing methods for this store, based on the plans defined in the `billing` config option.\n\n\n\n\n" - }, - { - "filePath": "src/server/authenticate/admin/types.ts", + "filePath": "src/server/authenticate/public/checkout/types.ts", "syntaxKind": "PropertySignature", "name": "cors", "value": "EnsureCORSFunction", "description": "A function that ensures the CORS headers are set correctly for the response.", "examples": [ { - "title": "Setting CORS headers for a admin request", - "description": "Use the `cors` helper to ensure your app can respond to requests from admin extensions.", + "title": "Setting CORS headers for a public request", + "description": "Use the `cors` helper to ensure your app can respond to checkout extension requests.", "tabs": [ { - "code": "import { LoaderFunctionArgs, json } from \"@remix-run/node\";\nimport { authenticate } from \"../shopify.server\";\nimport { getMyAppData } from \"~/db/model.server\";\n\nexport const loader = async ({ request }: LoaderFunctionArgs) => {\n const { session, cors } = await authenticate.admin(request);\n return cors(json(await getMyAppData({user: session.onlineAccessInfo!.id})));\n};", - "title": "/app/routes/admin/my-route.ts" + "code": "import { LoaderFunctionArgs, json } from \"@remix-run/node\";\nimport { authenticate } from \"../shopify.server\";\nimport { getMyAppData } from \"~/db/model.server\";\n\nexport const loader = async ({ request }: LoaderFunctionArgs) => {\n const { sessionToken, cors } = await authenticate.public.checkout(\n request,\n { corsHeaders: [\"X-My-Custom-Header\"] }\n );\n const data = await getMyAppData({shop: sessionToken.dest});\n return cors(json(data));\n};", + "title": "app/routes/public/my-route.ts" } ] } ] } ], - "value": "export interface NonEmbeddedAdminContext<\n Config extends AppConfigArg,\n Resources extends ShopifyRestResources = ShopifyRestResources,\n> extends AdminContextInternal {}" + "value": "export interface CheckoutContext {\n /**\n * The decoded and validated session token for the request\n *\n * Refer to the OAuth docs for the [session token payload](https://shopify.dev/docs/apps/auth/oauth/session-tokens#payload).\n *\n * @example\n * Using the decoded session token.\n * Get store-specific data using the `sessionToken` object.\n * ```ts\n * // app/routes/public/my-route.ts\n * import { LoaderFunctionArgs, json } from \"@remix-run/node\";\n * import { authenticate } from \"../shopify.server\";\n * import { getMyAppData } from \"~/db/model.server\";\n *\n * export const loader = async ({ request }: LoaderFunctionArgs) => {\n * const { sessionToken } = await authenticate.public.checkout(\n * request\n * );\n * return json(await getMyAppData({shop: sessionToken.dest}));\n * };\n * ```\n */\n sessionToken: JwtPayload;\n\n /**\n * A function that ensures the CORS headers are set correctly for the response.\n *\n * @example\n * Setting CORS headers for a public request.\n * Use the `cors` helper to ensure your app can respond to checkout extension requests.\n * ```ts\n * // app/routes/public/my-route.ts\n * import { LoaderFunctionArgs, json } from \"@remix-run/node\";\n * import { authenticate } from \"../shopify.server\";\n * import { getMyAppData } from \"~/db/model.server\";\n *\n * export const loader = async ({ request }: LoaderFunctionArgs) => {\n * const { sessionToken, cors } = await authenticate.public.checkout(\n * request,\n * { corsHeaders: [\"X-My-Custom-Header\"] }\n * );\n * const data = await getMyAppData({shop: sessionToken.dest});\n * return cors(json(data));\n * };\n * ```\n */\n cors: EnsureCORSFunction;\n}" }, - "BillingContext": { - "filePath": "src/server/authenticate/admin/billing/types.ts", - "name": "BillingContext", + "AuthenticateAppProxy": { + "filePath": "src/server/authenticate/public/appProxy/types.ts", + "name": "AuthenticateAppProxy", + "description": "", + "params": [ + { + "name": "request", + "description": "", + "value": "Request", + "filePath": "src/server/authenticate/public/appProxy/types.ts" + } + ], + "returns": { + "filePath": "src/server/authenticate/public/appProxy/types.ts", + "description": "", + "name": "Promise", + "value": "Promise" + }, + "value": "export type AuthenticateAppProxy = (\n request: Request,\n) => Promise;" + }, + "AppProxyContext": { + "filePath": "src/server/authenticate/public/appProxy/types.ts", + "name": "AppProxyContext", "description": "", "members": [ { - "filePath": "src/server/authenticate/admin/billing/types.ts", + "filePath": "src/server/authenticate/public/appProxy/types.ts", "syntaxKind": "PropertySignature", - "name": "require", - "value": "(options: RequireBillingOptions) => Promise", - "description": "Checks if the shop has an active payment for any plan defined in the `billing` config option.", + "name": "session", + "value": "undefined", + "description": "No session is available for the shop that made this request.\n\nThis comes from the session storage which `shopifyApp` uses to store sessions in your database of choice." + }, + { + "filePath": "src/server/authenticate/public/appProxy/types.ts", + "syntaxKind": "PropertySignature", + "name": "admin", + "value": "undefined", + "description": "No session is available for the shop that made this request. Therefore no methods for interacting with the GraphQL / REST Admin APIs are available." + }, + { + "filePath": "src/server/authenticate/public/appProxy/types.ts", + "syntaxKind": "PropertySignature", + "name": "storefront", + "value": "undefined", + "description": "No session is available for the shop that made this request. Therefore no method for interacting with the Storefront API is available." + }, + { + "filePath": "src/server/authenticate/public/appProxy/types.ts", + "syntaxKind": "PropertySignature", + "name": "liquid", + "value": "LiquidResponseFunction", + "description": "A utility for creating a Liquid Response.", "examples": [ { - "title": "Requesting billing right away", - "description": "Call `billing.request` in the `onFailure` callback to immediately redirect to the Shopify page to request payment.", - "tabs": [ - { - "code": "import { LoaderFunctionArgs } from \"@remix-run/node\";\nimport { authenticate, MONTHLY_PLAN } from \"../shopify.server\";\n\nexport const loader = async ({ request }: LoaderFunctionArgs) => {\n const { billing } = await authenticate.admin(request);\n await billing.require({\n plans: [MONTHLY_PLAN],\n isTest: true,\n onFailure: async () => billing.request({ plan: MONTHLY_PLAN }),\n });\n\n // App logic\n};", - "title": "/app/routes/**\\/*.ts" - }, - { - "code": "import { shopifyApp, BillingInterval } from \"@shopify/shopify-app-remix/server\";\n\nexport const MONTHLY_PLAN = 'Monthly subscription';\nexport const ANNUAL_PLAN = 'Annual subscription';\n\nconst shopify = shopifyApp({\n // ...etc\n billing: {\n [MONTHLY_PLAN]: {\n amount: 5,\n currencyCode: 'USD',\n interval: BillingInterval.Every30Days,\n },\n [ANNUAL_PLAN]: {\n amount: 50,\n currencyCode: 'USD',\n interval: BillingInterval.Annual,\n },\n }\n});\nexport default shopify;\nexport const authenticate = shopify.authenticate;", - "title": "shopify.server.ts" - } - ] - }, - { - "title": "Redirect to a plan selection page", - "description": "When the app has multiple plans, create a page in your App that allows the merchant to select a plan. If a merchant does not have the required plan you can redirect them to page in your app to select one.", + "title": "Rendering liquid content", + "description": "Use the `liquid` helper to render a `Response` with Liquid content using the shop's theme. See the [Liquid reference](https://shopify.dev/docs/api/liquid) for all the features it enables.", "tabs": [ { - "code": "import { LoaderFunctionArgs, redirect } from \"@remix-run/node\";\nimport { authenticate, MONTHLY_PLAN, ANNUAL_PLAN } from \"../shopify.server\";\n\nexport const loader = async ({ request }: LoaderFunctionArgs) => {\n const { billing } = await authenticate.admin(request);\n const billingCheck = await billing.require({\n plans: [MONTHLY_PLAN, ANNUAL_PLAN],\n isTest: true,\n onFailure: () => redirect('/select-plan'),\n });\n\n const subscription = billingCheck.appSubscriptions[0];\n console.log(`Shop is on ${subscription.name} (id ${subscription.id})`);\n\n // App logic\n};", - "title": "/app/routes/**\\/*.ts" - }, - { - "code": "import { shopifyApp, BillingInterval } from \"@shopify/shopify-app-remix/server\";\n\nexport const MONTHLY_PLAN = 'Monthly subscription';\nexport const ANNUAL_PLAN = 'Annual subscription';\n\nconst shopify = shopifyApp({\n // ...etc\n billing: {\n [MONTHLY_PLAN]: {\n amount: 5,\n currencyCode: 'USD',\n interval: BillingInterval.Every30Days,\n },\n [ANNUAL_PLAN]: {\n amount: 50,\n currencyCode: 'USD',\n interval: BillingInterval.Annual,\n },\n }\n});\nexport default shopify;\nexport const authenticate = shopify.authenticate;", - "title": "shopify.server.ts" + "code": "import {authenticate} from \"~/shopify.server\"\n\nexport async function loader({ request }) {\n const {liquid} = await authenticate.public.appProxy(request);\n\n return liquid(\"Hello {{shop.name}}\");\n}", + "title": "app/routes/**\\/.ts" } ] }, { - "title": "Requesting billing with line items", - "description": "Call `billing.request` with the `v3_lineItemBilling` future flag enabled", + "title": "Rendering liquid content without a layout", + "description": "Set the `layout` option to `false` to render the Liquid content without a theme.", "tabs": [ { - "code": "import { LoaderFunctionArgs } from \"@remix-run/node\";\nimport { authenticate, MONTHLY_PLAN } from \"../shopify.server\";\n\nexport const loader = async ({ request }: LoaderFunctionArgs) => {\n const { billing } = await authenticate.admin(request);\n await billing.require({\n plans: [MONTHLY_PLAN],\n isTest: true,\n onFailure: async () => billing.request({ plan: MONTHLY_PLAN }),\n });\n\n // App logic\n};", - "title": "/app/routes/**\\/*.ts" - }, - { - "code": "import { shopifyApp, BillingInterval } from \"@shopify/shopify-app-remix/server\";\n\nexport const MONTHLY_PLAN = 'Monthly subscription';\nexport const ANNUAL_PLAN = 'Annual subscription';\n\nconst shopify = shopifyApp({\n // ...etc\n billing: {\n [MONTHLY_PLAN]: {\n lineItems: [\n {\n amount: 5,\n currencyCode: 'USD',\n interval: BillingInterval.Every30Days,\n }\n {\n amount: 1,\n currencyCode: 'USD',\n interval: BillingInterval.Usage.\n terms: '1 dollar per 1000 emails',\n },\n ],\n },\n }\n future: {v3_lineItemBilling: true}\n});\nexport default shopify;\nexport const authenticate = shopify.authenticate;", - "title": "shopify.server.ts" + "code": "import {authenticate} from \"~/shopify.server\"\n\nexport async function loader({ request }) {\n const {liquid} = await authenticate.public.appProxy(request);\n\n return liquid(\n \"Hello {{shop.name}}\",\n { layout: false }\n );\n}", + "title": "app/routes/**\\/.ts" } ] } ] + } + ], + "value": "export interface AppProxyContext extends Context {\n /**\n * No session is available for the shop that made this request.\n *\n * This comes from the session storage which `shopifyApp` uses to store sessions in your database of choice.\n */\n session: undefined;\n\n /**\n * No session is available for the shop that made this request.\n * Therefore no methods for interacting with the GraphQL / REST Admin APIs are available.\n */\n admin: undefined;\n\n /**\n * No session is available for the shop that made this request.\n * Therefore no method for interacting with the Storefront API is available.\n */\n storefront: undefined;\n}" + }, + "LiquidResponseFunction": { + "filePath": "src/server/authenticate/public/appProxy/types.ts", + "name": "LiquidResponseFunction", + "description": "", + "params": [ + { + "name": "body", + "description": "", + "value": "string", + "filePath": "src/server/authenticate/public/appProxy/types.ts" }, { - "filePath": "src/server/authenticate/admin/billing/types.ts", - "syntaxKind": "PropertySignature", - "name": "check", - "value": "(options: CheckBillingOptions) => Promise", - "description": "Checks if the shop has an active payment for any plan defined in the `billing` config option.", - "examples": [ - { - "title": "Check what billing plans a merchant is subscribed to", - "description": "Use billing.check if you want to determine which plans are in use. Unlike `require`, `check` does notthrow an error if no active billing plans are present.", - "tabs": [ - { - "code": "import { LoaderFunctionArgs } from \"@remix-run/node\";\nimport { authenticate, MONTHLY_PLAN } from \"../shopify.server\";\n\nexport const loader = async ({ request }: LoaderFunctionArgs) => {\n const { billing } = await authenticate.admin(request);\n const { hasActivePayment, appSubscriptions } = await billing.check({\n plans: [MONTHLY_PLAN],\n isTest: false,\n });\n console.log(hasActivePayment)\n console.log(appSubscriptions)\n};", - "title": "/app/routes/**\\/*.ts" - }, - { - "code": "import { shopifyApp, BillingInterval } from \"@shopify/shopify-app-remix/server\";\n\nexport const MONTHLY_PLAN = 'Monthly subscription';\nexport const ANNUAL_PLAN = 'Annual subscription';\n\nconst shopify = shopifyApp({\n // ...etc\n billing: {\n [MONTHLY_PLAN]: {\n amount: 5,\n currencyCode: 'USD',\n interval: BillingInterval.Every30Days,\n },\n [ANNUAL_PLAN]: {\n amount: 50,\n currencyCode: 'USD',\n interval: BillingInterval.Annual,\n },\n }\n});\nexport default shopify;\nexport const authenticate = shopify.authenticate;", - "title": "shopify.server.ts" + "name": "initAndOptions", + "description": "", + "value": "number | (ResponseInit & Options)", + "isOptional": true, + "filePath": "src/server/authenticate/public/appProxy/types.ts" + } + ], + "returns": { + "filePath": "src/server/authenticate/public/appProxy/types.ts", + "description": "", + "name": "Response", + "value": "Response" + }, + "value": "export type LiquidResponseFunction = (\n body: string,\n initAndOptions?: number | (ResponseInit & Options),\n) => Response;" + }, + "Options": { + "filePath": "src/server/authenticate/public/appProxy/types.ts", + "name": "Options", + "description": "", + "members": [ + { + "filePath": "src/server/authenticate/public/appProxy/types.ts", + "syntaxKind": "PropertySignature", + "name": "layout", + "value": "boolean", + "description": "Whether to use the shop's theme layout around the Liquid content.", + "isOptional": true + } + ], + "value": "interface Options {\n /**\n * Whether to use the shop's theme layout around the Liquid content.\n */\n layout?: boolean;\n}" + }, + "AppProxyContextWithSession": { + "filePath": "src/server/authenticate/public/appProxy/types.ts", + "name": "AppProxyContextWithSession", + "description": "", + "members": [ + { + "filePath": "src/server/authenticate/public/appProxy/types.ts", + "syntaxKind": "PropertySignature", + "name": "session", + "value": "Session", + "description": "The session for the shop that made the request.\n\nThis comes from the session storage which `shopifyApp` uses to store sessions in your database of choice.\n\nUse this to get shop or user-specific data.", + "examples": [ + { + "title": "Using the session object", + "description": "Get the session for the shop that initiated the request to the app proxy.", + "tabs": [ + { + "code": "import { json } from \"@remix-run/node\";\nimport { authenticate } from \"../shopify.server\";\nimport { getMyAppModelData } from \"~/db/model.server\";\n\nexport const loader = async ({ request }) => {\n // Get the session for the shop that initiated the request to the app proxy.\n const { session } =\n await authenticate.public.appProxy(request);\n\n // Use the session data to make to queries to your database or additional requests.\n return json(\n await getMyAppModelData({shop: session.shop})\n );\n};", + "title": "app/routes/**\\/.ts" } ] } ] }, { - "filePath": "src/server/authenticate/admin/billing/types.ts", + "filePath": "src/server/authenticate/public/appProxy/types.ts", "syntaxKind": "PropertySignature", - "name": "request", - "value": "(options: RequestBillingOptions) => Promise", - "description": "Requests payment for the plan.", + "name": "admin", + "value": "AdminApiContext", + "description": "Methods for interacting with the GraphQL / REST Admin APIs for the store that made the request.", "examples": [ { - "title": "Using a custom return URL", - "description": "Change where the merchant is returned to after approving the purchase using the `returnUrl` option.", + "title": "Interacting with the Admin API", + "description": "Use the `admin` object to interact with the REST or GraphQL APIs.", "tabs": [ { - "code": "import { LoaderFunctionArgs } from \"@remix-run/node\";\nimport { authenticate, MONTHLY_PLAN } from \"../shopify.server\";\n\nexport const loader = async ({ request }: LoaderFunctionArgs) => {\n const { billing } = await authenticate.admin(request);\n await billing.require({\n plans: [MONTHLY_PLAN],\n onFailure: async () => billing.request({\n plan: MONTHLY_PLAN,\n isTest: true,\n returnUrl: 'https://admin.shopify.com/store/my-store/apps/my-app/billing-page',\n }),\n });\n\n // App logic\n};", - "title": "/app/routes/**\\/*.ts" - }, + "code": "import { json } from \"@remix-run/node\";\nimport { authenticate } from \"../shopify.server\";\n\nexport async function action({ request }: ActionFunctionArgs) {\n const { admin } = await authenticate.public.appProxy(request);\n\n const response = await admin.graphql(\n `#graphql\n mutation populateProduct($input: ProductInput!) {\n productCreate(input: $input) {\n product {\n id\n }\n }\n }`,\n {\n variables: {\n input: { title: \"Product Name\" }\n }\n }\n );\n\n const productData = await response.json();\n return json({ data: productData.data });\n}", + "title": "app/routes/**\\/.ts" + } + ] + } + ] + }, + { + "filePath": "src/server/authenticate/public/appProxy/types.ts", + "syntaxKind": "PropertySignature", + "name": "storefront", + "value": "StorefrontContext", + "description": "Method for interacting with the Shopify Storefront Graphql API for the store that made the request.", + "examples": [ + { + "title": "Interacting with the Storefront API", + "description": "Use the `storefront` object to interact with the GraphQL API.", + "tabs": [ { - "code": "import { shopifyApp, BillingInterval } from \"@shopify/shopify-app-remix/server\";\n\nexport const MONTHLY_PLAN = 'Monthly subscription';\nexport const ANNUAL_PLAN = 'Annual subscription';\n\nconst shopify = shopifyApp({\n // ...etc\n billing: {\n [MONTHLY_PLAN]: {\n amount: 5,\n currencyCode: 'USD',\n interval: BillingInterval.Every30Days,\n },\n [ANNUAL_PLAN]: {\n amount: 50,\n currencyCode: 'USD',\n interval: BillingInterval.Annual,\n },\n }\n});\nexport default shopify;\nexport const authenticate = shopify.authenticate;", - "title": "shopify.server.ts" + "code": "import { json } from \"@remix-run/node\";\nimport { authenticate } from \"../shopify.server\";\n\nexport async function action({ request }: ActionFunctionArgs) {\n const { storefront } = await authenticate.public.appProxy(request);\n\n const response = await storefront.graphql(\n `#graphql\n query blogIds {\n blogs(first: 10) {\n edges {\n node {\n id\n }\n }\n }\n }`\n );\n\n return json(await response.json());\n}", + "title": "app/routes/**\\/.ts" } ] } ] }, { - "filePath": "src/server/authenticate/admin/billing/types.ts", + "filePath": "src/server/authenticate/public/appProxy/types.ts", "syntaxKind": "PropertySignature", - "name": "cancel", - "value": "(options: CancelBillingOptions) => Promise", - "description": "Cancels an ongoing subscription, given its ID.", + "name": "liquid", + "value": "LiquidResponseFunction", + "description": "A utility for creating a Liquid Response.", "examples": [ { - "title": "Cancelling a subscription", - "description": "Use the `billing.cancel` function to cancel an active subscription with the id returned from `billing.require`.", + "title": "Rendering liquid content", + "description": "Use the `liquid` helper to render a `Response` with Liquid content using the shop's theme. See the [Liquid reference](https://shopify.dev/docs/api/liquid) for all the features it enables.", "tabs": [ { - "code": "import { LoaderFunctionArgs } from \"@remix-run/node\";\nimport { authenticate, MONTHLY_PLAN } from \"../shopify.server\";\n\nexport const loader = async ({ request }: LoaderFunctionArgs) => {\n const { billing } = await authenticate.admin(request);\n const billingCheck = await billing.require({\n plans: [MONTHLY_PLAN],\n onFailure: async () => billing.request({ plan: MONTHLY_PLAN }),\n });\n\n const subscription = billingCheck.appSubscriptions[0];\n const cancelledSubscription = await billing.cancel({\n subscriptionId: subscription.id,\n isTest: true,\n prorate: true,\n });\n\n // App logic\n};", - "title": "/app/routes/cancel-subscription.ts" - }, + "code": "import {authenticate} from \"~/shopify.server\"\n\nexport async function loader({ request }) {\n const {liquid} = await authenticate.public.appProxy(request);\n\n return liquid(\"Hello {{shop.name}}\");\n}", + "title": "app/routes/**\\/.ts" + } + ] + }, + { + "title": "Rendering liquid content without a layout", + "description": "Set the `layout` option to `false` to render the Liquid content without a theme.", + "tabs": [ { - "code": "import { shopifyApp, BillingInterval } from \"@shopify/shopify-app-remix/server\";\n\nexport const MONTHLY_PLAN = 'Monthly subscription';\nexport const ANNUAL_PLAN = 'Annual subscription';\n\nconst shopify = shopifyApp({\n // ...etc\n billing: {\n [MONTHLY_PLAN]: {\n amount: 5,\n currencyCode: 'USD',\n interval: BillingInterval.Every30Days,\n },\n [ANNUAL_PLAN]: {\n amount: 50,\n currencyCode: 'USD',\n interval: BillingInterval.Annual,\n },\n }\n});\nexport default shopify;\nexport const authenticate = shopify.authenticate;", - "title": "shopify.server.ts" + "code": "import {authenticate} from \"~/shopify.server\"\n\nexport async function loader({ request }) {\n const {liquid} = await authenticate.public.appProxy(request);\n\n return liquid(\n \"Hello {{shop.name}}\",\n { layout: false }\n );\n}", + "title": "app/routes/**\\/.ts" } ] } ] } ], - "value": "export interface BillingContext {\n /**\n * Checks if the shop has an active payment for any plan defined in the `billing` config option.\n *\n * @returns A promise that resolves to an object containing the active purchases for the shop.\n *\n * @example\n * Requesting billing right away.\n * Call `billing.request` in the `onFailure` callback to immediately redirect to the Shopify page to request payment.\n * ```ts\n * // /app/routes/**\\/*.ts\n * import { LoaderFunctionArgs } from \"@remix-run/node\";\n * import { authenticate, MONTHLY_PLAN } from \"../shopify.server\";\n *\n * export const loader = async ({ request }: LoaderFunctionArgs) => {\n * const { billing } = await authenticate.admin(request);\n * await billing.require({\n * plans: [MONTHLY_PLAN],\n * isTest: true,\n * onFailure: async () => billing.request({ plan: MONTHLY_PLAN }),\n * });\n *\n * // App logic\n * };\n * ```\n * ```ts\n * // shopify.server.ts\n * import { shopifyApp, BillingInterval } from \"@shopify/shopify-app-remix/server\";\n *\n * export const MONTHLY_PLAN = 'Monthly subscription';\n * export const ANNUAL_PLAN = 'Annual subscription';\n *\n * const shopify = shopifyApp({\n * // ...etc\n * billing: {\n * [MONTHLY_PLAN]: {\n * amount: 5,\n * currencyCode: 'USD',\n * interval: BillingInterval.Every30Days,\n * },\n * [ANNUAL_PLAN]: {\n * amount: 50,\n * currencyCode: 'USD',\n * interval: BillingInterval.Annual,\n * },\n * }\n * });\n * export default shopify;\n * export const authenticate = shopify.authenticate;\n * ```\n *\n * @example\n * Redirect to a plan selection page.\n * When the app has multiple plans, create a page in your App that allows the merchant to select a plan. If a merchant does not have the required plan you can redirect them to page in your app to select one.\n * ```ts\n * // /app/routes/**\\/*.ts\n * import { LoaderFunctionArgs, redirect } from \"@remix-run/node\";\n * import { authenticate, MONTHLY_PLAN, ANNUAL_PLAN } from \"../shopify.server\";\n *\n * export const loader = async ({ request }: LoaderFunctionArgs) => {\n * const { billing } = await authenticate.admin(request);\n * const billingCheck = await billing.require({\n * plans: [MONTHLY_PLAN, ANNUAL_PLAN],\n * isTest: true,\n * onFailure: () => redirect('/select-plan'),\n * });\n *\n * const subscription = billingCheck.appSubscriptions[0];\n * console.log(`Shop is on ${subscription.name} (id ${subscription.id})`);\n *\n * // App logic\n * };\n * ```\n * ```ts\n * // shopify.server.ts\n * import { shopifyApp, BillingInterval } from \"@shopify/shopify-app-remix/server\";\n *\n * export const MONTHLY_PLAN = 'Monthly subscription';\n * export const ANNUAL_PLAN = 'Annual subscription';\n *\n * const shopify = shopifyApp({\n * // ...etc\n * billing: {\n * [MONTHLY_PLAN]: {\n * amount: 5,\n * currencyCode: 'USD',\n * interval: BillingInterval.Every30Days,\n * },\n * [ANNUAL_PLAN]: {\n * amount: 50,\n * currencyCode: 'USD',\n * interval: BillingInterval.Annual,\n * },\n * }\n * });\n * export default shopify;\n * export const authenticate = shopify.authenticate;\n * ```\n * @example\n * Requesting billing with line items\n * Call `billing.request` with the `v3_lineItemBilling` future flag enabled\n * ```ts\n * // /app/routes/**\\/*.ts\n * import { LoaderFunctionArgs } from \"@remix-run/node\";\n * import { authenticate, MONTHLY_PLAN } from \"../shopify.server\";\n *\n * export const loader = async ({ request }: LoaderFunctionArgs) => {\n * const { billing } = await authenticate.admin(request);\n * await billing.require({\n * plans: [MONTHLY_PLAN],\n * isTest: true,\n * onFailure: async () => billing.request({ plan: MONTHLY_PLAN }),\n * });\n *\n * // App logic\n * };\n * ```\n * ```ts\n * // shopify.server.ts\n * import { shopifyApp, BillingInterval } from \"@shopify/shopify-app-remix/server\";\n *\n * export const MONTHLY_PLAN = 'Monthly subscription';\n * export const ANNUAL_PLAN = 'Annual subscription';\n *\n * const shopify = shopifyApp({\n * // ...etc\n * billing: {\n * [MONTHLY_PLAN]: {\n * lineItems: [\n * {\n * amount: 5,\n * currencyCode: 'USD',\n * interval: BillingInterval.Every30Days,\n * }\n * {\n * amount: 1,\n * currencyCode: 'USD',\n * interval: BillingInterval.Usage.\n * terms: '1 dollar per 1000 emails',\n * },\n * ],\n * },\n * }\n * future: {v3_lineItemBilling: true}\n * });\n * export default shopify;\n * export const authenticate = shopify.authenticate;\n * ```\n */\n require: (\n options: RequireBillingOptions,\n ) => Promise;\n\n /**\n * Checks if the shop has an active payment for any plan defined in the `billing` config option.\n *\n * @returns A promise that resolves to an object containing the active purchases for the shop.\n *\n * @example\n * Check what billing plans a merchant is subscribed to.\n * Use billing.check if you want to determine which plans are in use. Unlike `require`, `check` does not\n * throw an error if no active billing plans are present. \n * ```ts\n * // /app/routes/**\\/*.ts\n * import { LoaderFunctionArgs } from \"@remix-run/node\";\n * import { authenticate, MONTHLY_PLAN } from \"../shopify.server\";\n *\n * export const loader = async ({ request }: LoaderFunctionArgs) => {\n * const { billing } = await authenticate.admin(request);\n * const { hasActivePayment, appSubscriptions } = await billing.check({\n * plans: [MONTHLY_PLAN],\n * isTest: false,\n * });\n * console.log(hasActivePayment)\n * console.log(appSubscriptions)\n * };\n * ```\n * ```ts\n * // shopify.server.ts\n * import { shopifyApp, BillingInterval } from \"@shopify/shopify-app-remix/server\";\n *\n * export const MONTHLY_PLAN = 'Monthly subscription';\n * export const ANNUAL_PLAN = 'Annual subscription';\n *\n * const shopify = shopifyApp({\n * // ...etc\n * billing: {\n * [MONTHLY_PLAN]: {\n * amount: 5,\n * currencyCode: 'USD',\n * interval: BillingInterval.Every30Days,\n * },\n * [ANNUAL_PLAN]: {\n * amount: 50,\n * currencyCode: 'USD',\n * interval: BillingInterval.Annual,\n * },\n * }\n * });\n * export default shopify;\n * export const authenticate = shopify.authenticate;\n * ```\n *\n */\n check: (\n options: CheckBillingOptions,\n ) => Promise;\n\n /**\n * Requests payment for the plan.\n *\n * @returns Redirects to the confirmation URL for the payment.\n *\n * @example\n * Using a custom return URL.\n * Change where the merchant is returned to after approving the purchase using the `returnUrl` option.\n * ```ts\n * // /app/routes/**\\/*.ts\n * import { LoaderFunctionArgs } from \"@remix-run/node\";\n * import { authenticate, MONTHLY_PLAN } from \"../shopify.server\";\n *\n * export const loader = async ({ request }: LoaderFunctionArgs) => {\n * const { billing } = await authenticate.admin(request);\n * await billing.require({\n * plans: [MONTHLY_PLAN],\n * onFailure: async () => billing.request({\n * plan: MONTHLY_PLAN,\n * isTest: true,\n * returnUrl: 'https://admin.shopify.com/store/my-store/apps/my-app/billing-page',\n * }),\n * });\n *\n * // App logic\n * };\n * ```\n * ```ts\n * // shopify.server.ts\n * import { shopifyApp, BillingInterval } from \"@shopify/shopify-app-remix/server\";\n *\n * export const MONTHLY_PLAN = 'Monthly subscription';\n * export const ANNUAL_PLAN = 'Annual subscription';\n *\n * const shopify = shopifyApp({\n * // ...etc\n * billing: {\n * [MONTHLY_PLAN]: {\n * amount: 5,\n * currencyCode: 'USD',\n * interval: BillingInterval.Every30Days,\n * },\n * [ANNUAL_PLAN]: {\n * amount: 50,\n * currencyCode: 'USD',\n * interval: BillingInterval.Annual,\n * },\n * }\n * });\n * export default shopify;\n * export const authenticate = shopify.authenticate;\n * ```\n */\n request: (options: RequestBillingOptions) => Promise;\n\n /**\n * Cancels an ongoing subscription, given its ID.\n *\n * @returns The cancelled subscription.\n *\n * @example\n * Cancelling a subscription.\n * Use the `billing.cancel` function to cancel an active subscription with the id returned from `billing.require`.\n * ```ts\n * // /app/routes/cancel-subscription.ts\n * import { LoaderFunctionArgs } from \"@remix-run/node\";\n * import { authenticate, MONTHLY_PLAN } from \"../shopify.server\";\n *\n * export const loader = async ({ request }: LoaderFunctionArgs) => {\n * const { billing } = await authenticate.admin(request);\n * const billingCheck = await billing.require({\n * plans: [MONTHLY_PLAN],\n * onFailure: async () => billing.request({ plan: MONTHLY_PLAN }),\n * });\n *\n * const subscription = billingCheck.appSubscriptions[0];\n * const cancelledSubscription = await billing.cancel({\n * subscriptionId: subscription.id,\n * isTest: true,\n * prorate: true,\n * });\n *\n * // App logic\n * };\n * ```\n * ```ts\n * // shopify.server.ts\n * import { shopifyApp, BillingInterval } from \"@shopify/shopify-app-remix/server\";\n *\n * export const MONTHLY_PLAN = 'Monthly subscription';\n * export const ANNUAL_PLAN = 'Annual subscription';\n *\n * const shopify = shopifyApp({\n * // ...etc\n * billing: {\n * [MONTHLY_PLAN]: {\n * amount: 5,\n * currencyCode: 'USD',\n * interval: BillingInterval.Every30Days,\n * },\n * [ANNUAL_PLAN]: {\n * amount: 50,\n * currencyCode: 'USD',\n * interval: BillingInterval.Annual,\n * },\n * }\n * });\n * export default shopify;\n * export const authenticate = shopify.authenticate;\n * ```\n */\n cancel: (options: CancelBillingOptions) => Promise;\n}" + "value": "export interface AppProxyContextWithSession<\n Resources extends ShopifyRestResources = ShopifyRestResources,\n> extends Context {\n /**\n * The session for the shop that made the request.\n *\n * This comes from the session storage which `shopifyApp` uses to store sessions in your database of choice.\n *\n * Use this to get shop or user-specific data.\n *\n * @example\n * Using the session object.\n * Get the session for the shop that initiated the request to the app proxy.\n * ```ts\n * // app/routes/**\\/.ts\n * import { json } from \"@remix-run/node\";\n * import { authenticate } from \"../shopify.server\";\n * import { getMyAppModelData } from \"~/db/model.server\";\n *\n * export const loader = async ({ request }) => {\n * // Get the session for the shop that initiated the request to the app proxy.\n * const { session } =\n * await authenticate.public.appProxy(request);\n *\n * // Use the session data to make to queries to your database or additional requests.\n * return json(\n * await getMyAppModelData({shop: session.shop})\n * );\n * };\n * ```\n */\n session: Session;\n\n /**\n * Methods for interacting with the GraphQL / REST Admin APIs for the store that made the request.\n *\n * @example\n * Interacting with the Admin API.\n * Use the `admin` object to interact with the REST or GraphQL APIs.\n * ```ts\n * // app/routes/**\\/.ts\n * import { json } from \"@remix-run/node\";\n * import { authenticate } from \"../shopify.server\";\n *\n * export async function action({ request }: ActionFunctionArgs) {\n * const { admin } = await authenticate.public.appProxy(request);\n *\n * const response = await admin.graphql(\n * `#graphql\n * mutation populateProduct($input: ProductInput!) {\n * productCreate(input: $input) {\n * product {\n * id\n * }\n * }\n * }`,\n * {\n * variables: {\n * input: { title: \"Product Name\" }\n * }\n * }\n * );\n *\n * const productData = await response.json();\n * return json({ data: productData.data });\n * }\n * ```\n */\n admin: AdminApiContext;\n\n /**\n * Method for interacting with the Shopify Storefront Graphql API for the store that made the request.\n *\n * @example\n * Interacting with the Storefront API.\n * Use the `storefront` object to interact with the GraphQL API.\n * ```ts\n * // app/routes/**\\/.ts\n * import { json } from \"@remix-run/node\";\n * import { authenticate } from \"../shopify.server\";\n *\n * export async function action({ request }: ActionFunctionArgs) {\n * const { storefront } = await authenticate.public.appProxy(request);\n *\n * const response = await storefront.graphql(\n * `#graphql\n * query blogIds {\n * blogs(first: 10) {\n * edges {\n * node {\n * id\n * }\n * }\n * }\n * }`\n * );\n *\n * return json(await response.json());\n * }\n * ```\n */\n storefront: StorefrontContext;\n}" }, - "RequireBillingOptions": { - "filePath": "src/server/authenticate/admin/billing/types.ts", - "name": "RequireBillingOptions", + "StorefrontContext": { + "filePath": "src/server/clients/storefront/types.ts", + "name": "StorefrontContext", "description": "", "members": [ { - "filePath": "src/server/authenticate/admin/billing/types.ts", - "syntaxKind": "PropertySignature", - "name": "plans", - "value": "(keyof Config[\"billing\"])[]", - "description": "The plans to check for. Must be one of the values defined in the `billing` config option." - }, - { - "filePath": "src/server/authenticate/admin/billing/types.ts", - "syntaxKind": "PropertySignature", - "name": "onFailure", - "value": "(error: any) => Promise", - "description": "How to handle the request if the shop doesn't have an active payment for any plan." - }, - { - "filePath": "src/server/authenticate/admin/billing/types.ts", + "filePath": "src/server/clients/storefront/types.ts", "syntaxKind": "PropertySignature", - "name": "isTest", - "value": "boolean", - "description": "Whether to consider test purchases.", - "isOptional": true + "name": "graphql", + "value": "GraphQLClient", + "description": "Method for interacting with the Shopify Storefront GraphQL API\n\nIf 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).\n\n\n\n\n", + "examples": [ + { + "title": "Querying the GraphQL API", + "description": "Use `storefront.graphql` to make query / mutation requests.", + "tabs": [ + { + "code": "import { json } from \"@remix-run/node\";\nimport { authenticate } from \"../shopify.server\";\n\nexport async function action({ request }: ActionFunctionArgs) {\n const { storefront } = await authenticate.public.appProxy(request);\n\n const response = await storefront.graphql(`{blogs(first: 10) { edges { node { id } } } }`);\n\n return json(await response.json());\n}", + "title": "app/routes/**\\/.ts" + } + ] + }, + { + "title": "Handling GraphQL errors", + "description": "Catch `GraphqlQueryError` errors to see error messages from the API.", + "tabs": [ + { + "code": "import { ActionFunctionArgs } from \"@remix-run/node\";\nimport { authenticate } from \"../shopify.server\";\n\nexport const action = async ({ request }: ActionFunctionArgs) => {\n const { storefront } = await authenticate.public.appProxy(request);\n\n try {\n const response = await storefront.graphql(\n `#graphql\n query incorrectQuery {\n products(first: 10) {\n nodes {\n not_a_field\n }\n }\n }`,\n );\n\n return json({ data: await response.json() });\n } catch (error) {\n if (error instanceof GraphqlQueryError) {\n // { errors: { graphQLErrors: [\n // { message: \"Field 'not_a_field' doesn't exist on type 'Product'\" }\n // ] } }\n return json({ errors: error.body?.errors }, { status: 500 });\n }\n return json({ message: \"An error occurred\" }, { status: 500 });\n }\n}", + "title": "/app/routes/**\\/*.ts" + }, + { + "code": "import { shopifyApp } from \"@shopify/shopify-app-remix/server\";\n\nconst shopify = shopifyApp({\n // ...\n});\nexport default shopify;\nexport const authenticate = shopify.authenticate;", + "title": "/app/shopify.server.ts" + } + ] + } + ] } ], - "value": "export interface RequireBillingOptions\n extends Omit {\n /**\n * The plans to check for. Must be one of the values defined in the `billing` config option.\n */\n plans: (keyof Config['billing'])[];\n /**\n * How to handle the request if the shop doesn't have an active payment for any plan.\n */\n onFailure: (error: any) => Promise;\n}" + "value": "export interface StorefrontContext {\n /**\n * Method for interacting with the Shopify Storefront GraphQL API\n *\n * 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).\n *\n * {@link https://shopify.dev/docs/api/storefront}\n *\n * @example\n * Querying the GraphQL API.\n * Use `storefront.graphql` to make query / mutation requests.\n * ```ts\n * // app/routes/**\\/.ts\n * import { json } from \"@remix-run/node\";\n * import { authenticate } from \"../shopify.server\";\n *\n * export async function action({ request }: ActionFunctionArgs) {\n * const { storefront } = await authenticate.public.appProxy(request);\n *\n * const response = await storefront.graphql(`{blogs(first: 10) { edges { node { id } } } }`);\n *\n * return json(await response.json());\n * }\n * ```\n *\n * @example\n * Handling GraphQL errors.\n * Catch `GraphqlQueryError` errors to see error messages from the API.\n * ```ts\n * // /app/routes/**\\/*.ts\n * import { ActionFunctionArgs } from \"@remix-run/node\";\n * import { authenticate } from \"../shopify.server\";\n *\n * export const action = async ({ request }: ActionFunctionArgs) => {\n * const { storefront } = await authenticate.public.appProxy(request);\n *\n * try {\n * const response = await storefront.graphql(\n * `#graphql\n * query incorrectQuery {\n * products(first: 10) {\n * nodes {\n * not_a_field\n * }\n * }\n * }`,\n * );\n *\n * return json({ data: await response.json() });\n * } catch (error) {\n * if (error instanceof GraphqlQueryError) {\n * // { errors: { graphQLErrors: [\n * // { message: \"Field 'not_a_field' doesn't exist on type 'Product'\" }\n * // ] } }\n * return json({ errors: error.body?.errors }, { status: 500 });\n * }\n * return json({ message: \"An error occurred\" }, { status: 500 });\n * }\n * }\n * ```\n *\n * ```ts\n * // /app/shopify.server.ts\n * import { shopifyApp } from \"@shopify/shopify-app-remix/server\";\n *\n * const shopify = shopifyApp({\n * // ...\n * });\n * export default shopify;\n * export const authenticate = shopify.authenticate;\n * ```\n */\n graphql: GraphQLClient;\n}" }, - "BillingCheckResponseObject": { - "filePath": "../../node_modules/@shopify/shopify-api/lib/billing/types.d.ts", - "name": "BillingCheckResponseObject", + "StorefrontOperations": { + "filePath": "../../node_modules/@shopify/shopify-api/node_modules/@shopify/storefront-api-client/dist/storefront-api-client.d.ts", + "syntaxKind": "TypeAliasDeclaration", + "name": "StorefrontOperations", + "value": "StorefrontQueries & StorefrontMutations", + "description": "" + }, + "StorefrontQueries": { + "filePath": "../../node_modules/@shopify/shopify-api/node_modules/@shopify/storefront-api-client/dist/storefront-api-client.d.ts", + "name": "StorefrontQueries", "description": "", "members": [ { - "filePath": "../../node_modules/@shopify/shopify-api/lib/billing/types.d.ts", - "syntaxKind": "PropertySignature", - "name": "hasActivePayment", - "value": "boolean", - "description": "Whether the user has an active payment method." - }, - { - "filePath": "../../node_modules/@shopify/shopify-api/lib/billing/types.d.ts", - "syntaxKind": "PropertySignature", - "name": "oneTimePurchases", - "value": "OneTimePurchase[]", - "description": "The one-time purchases the shop has." + "filePath": "../../node_modules/@shopify/shopify-api/node_modules/@shopify/storefront-api-client/dist/storefront-api-client.d.ts", + "name": "[key: string]", + "value": "{\n variables: any;\n return: any;\n }" }, { - "filePath": "../../node_modules/@shopify/shopify-api/lib/billing/types.d.ts", - "syntaxKind": "PropertySignature", - "name": "appSubscriptions", - "value": "AppSubscription[]", - "description": "The active subscriptions the shop has." + "filePath": "../../node_modules/@shopify/shopify-api/node_modules/@shopify/storefront-api-client/dist/storefront-api-client.d.ts", + "name": "[key: number | symbol]", + "value": "never" } ], - "value": "export interface BillingCheckResponseObject {\n /**\n * Whether the user has an active payment method.\n */\n hasActivePayment: boolean;\n /**\n * The one-time purchases the shop has.\n */\n oneTimePurchases: OneTimePurchase[];\n /**\n * The active subscriptions the shop has.\n */\n appSubscriptions: AppSubscription[];\n}" + "value": "interface StorefrontQueries {\n [key: string]: {\n variables: any;\n return: any;\n };\n [key: number | symbol]: never;\n}" }, - "OneTimePurchase": { - "filePath": "../../node_modules/@shopify/shopify-api/lib/billing/types.d.ts", - "name": "OneTimePurchase", + "StorefrontMutations": { + "filePath": "../../node_modules/@shopify/shopify-api/node_modules/@shopify/storefront-api-client/dist/storefront-api-client.d.ts", + "name": "StorefrontMutations", "description": "", "members": [ { - "filePath": "../../node_modules/@shopify/shopify-api/lib/billing/types.d.ts", - "syntaxKind": "PropertySignature", - "name": "id", - "value": "string", - "description": "The ID of the one-time purchase." - }, - { - "filePath": "../../node_modules/@shopify/shopify-api/lib/billing/types.d.ts", - "syntaxKind": "PropertySignature", - "name": "name", - "value": "string", - "description": "The name of the purchased plan." - }, - { - "filePath": "../../node_modules/@shopify/shopify-api/lib/billing/types.d.ts", - "syntaxKind": "PropertySignature", - "name": "test", - "value": "boolean", - "description": "Whether this is a test purchase." + "filePath": "../../node_modules/@shopify/shopify-api/node_modules/@shopify/storefront-api-client/dist/storefront-api-client.d.ts", + "name": "[key: string]", + "value": "{\n variables: any;\n return: any;\n }" }, { - "filePath": "../../node_modules/@shopify/shopify-api/lib/billing/types.d.ts", - "syntaxKind": "PropertySignature", - "name": "status", - "value": "string", - "description": "The status of the one-time purchase." + "filePath": "../../node_modules/@shopify/shopify-api/node_modules/@shopify/storefront-api-client/dist/storefront-api-client.d.ts", + "name": "[key: number | symbol]", + "value": "never" } ], - "value": "export interface OneTimePurchase {\n /**\n * The ID of the one-time purchase.\n */\n id: string;\n /**\n * The name of the purchased plan.\n */\n name: string;\n /**\n * Whether this is a test purchase.\n */\n test: boolean;\n /**\n * The status of the one-time purchase.\n */\n status: string;\n}" + "value": "interface StorefrontMutations {\n [key: string]: {\n variables: any;\n return: any;\n };\n [key: number | symbol]: never;\n}" }, - "AppSubscription": { - "filePath": "../../node_modules/@shopify/shopify-api/lib/billing/types.d.ts", - "name": "AppSubscription", + "AuthenticatePublicLegacy": { + "filePath": "src/server/authenticate/public/types.ts", + "syntaxKind": "TypeAliasDeclaration", + "name": "AuthenticatePublicLegacy", + "value": "AuthenticateCheckout & AuthenticatePublicObject", + "description": "Methods for authenticating Requests from Shopify's public surfaces\n\nTo maintain backwards compatability this is a function and an object.\n\nDo not use `authenticate.public()`. Use `authenticate.public.checkout()` instead. `authenticate.public()` will be removed in v2.\n\nMethods are:\n\n- `authenticate.public.checkout()` for authenticating requests from checkout extensions - `authenticate.public.appProxy()` for authenticating requests from app proxies" + }, + "AuthenticateWebhook": { + "filePath": "src/server/authenticate/webhooks/types.ts", + "name": "AuthenticateWebhook", "description": "", - "members": [ + "params": [ { - "filePath": "../../node_modules/@shopify/shopify-api/lib/billing/types.d.ts", - "syntaxKind": "PropertySignature", - "name": "id", - "value": "string", - "description": "The ID of the app subscription." - }, - { - "filePath": "../../node_modules/@shopify/shopify-api/lib/billing/types.d.ts", - "syntaxKind": "PropertySignature", - "name": "name", - "value": "string", - "description": "The name of the purchased plan." - }, - { - "filePath": "../../node_modules/@shopify/shopify-api/lib/billing/types.d.ts", - "syntaxKind": "PropertySignature", - "name": "test", - "value": "boolean", - "description": "Whether this is a test subscription." + "name": "request", + "description": "", + "value": "Request", + "filePath": "src/server/authenticate/webhooks/types.ts" } ], - "value": "export interface AppSubscription {\n /**\n * The ID of the app subscription.\n */\n id: string;\n /**\n * The name of the purchased plan.\n */\n name: string;\n /**\n * Whether this is a test subscription.\n */\n test: boolean;\n}" + "returns": { + "filePath": "src/server/authenticate/webhooks/types.ts", + "description": "", + "name": "Promise>", + "value": "Promise>" + }, + "value": "export type AuthenticateWebhook<\n Future extends FutureFlagOptions,\n Resources extends ShopifyRestResources,\n Topics = string | number | symbol,\n> = (request: Request) => Promise>;" }, - "CheckBillingOptions": { - "filePath": "src/server/authenticate/admin/billing/types.ts", - "name": "CheckBillingOptions", - "description": "", - "members": [ - { - "filePath": "src/server/authenticate/admin/billing/types.ts", - "syntaxKind": "PropertySignature", - "name": "plans", - "value": "(keyof Config[\"billing\"])[]", - "description": "The plans to check for. Must be one of the values defined in the `billing` config option." - }, - { - "filePath": "src/server/authenticate/admin/billing/types.ts", - "syntaxKind": "PropertySignature", - "name": "isTest", - "value": "boolean", - "description": "Whether to consider test purchases.", - "isOptional": true - } - ], - "value": "export interface CheckBillingOptions\n extends Omit {\n /**\n * The plans to check for. Must be one of the values defined in the `billing` config option.\n */\n plans: (keyof Config['billing'])[];\n}" + "WebhookContext": { + "filePath": "src/server/authenticate/webhooks/types.ts", + "syntaxKind": "TypeAliasDeclaration", + "name": "WebhookContext", + "value": "WebhookContextWithoutSession | WebhookContextWithSession", + "description": "" }, - "RequestBillingOptions": { - "filePath": "src/server/authenticate/admin/billing/types.ts", - "name": "RequestBillingOptions", + "WebhookContextWithoutSession": { + "filePath": "src/server/authenticate/webhooks/types.ts", + "name": "WebhookContextWithoutSession", "description": "", "members": [ { - "filePath": "src/server/authenticate/admin/billing/types.ts", + "filePath": "src/server/authenticate/webhooks/types.ts", "syntaxKind": "PropertySignature", - "name": "plan", - "value": "keyof Config[\"billing\"]", - "description": "The plan to request. Must be one of the values defined in the `billing` config option." + "name": "session", + "value": "undefined", + "description": "" }, { - "filePath": "src/server/authenticate/admin/billing/types.ts", + "filePath": "src/server/authenticate/webhooks/types.ts", "syntaxKind": "PropertySignature", - "name": "isTest", - "value": "boolean", - "description": "Whether to use the test mode. This prevents the credit card from being charged. Test shops and demo shops cannot be charged.", - "isOptional": true + "name": "admin", + "value": "undefined", + "description": "" }, { - "filePath": "src/server/authenticate/admin/billing/types.ts", - "syntaxKind": "PropertySignature", - "name": "returnUrl", - "value": "string", - "description": "The URL to return to after the merchant approves the payment.", - "isOptional": true - } - ], - "value": "export interface RequestBillingOptions\n extends Omit {\n /**\n * The plan to request. Must be one of the values defined in the `billing` config option.\n */\n plan: keyof Config['billing'];\n /**\n * Whether to use the test mode. This prevents the credit card from being charged. Test shops and demo shops cannot be charged.\n */\n isTest?: boolean;\n /**\n * The URL to return to after the merchant approves the payment.\n */\n returnUrl?: string;\n}" - }, - "CancelBillingOptions": { - "filePath": "src/server/authenticate/admin/billing/types.ts", - "name": "CancelBillingOptions", - "description": "", - "members": [ - { - "filePath": "src/server/authenticate/admin/billing/types.ts", + "filePath": "src/server/authenticate/webhooks/types.ts", "syntaxKind": "PropertySignature", - "name": "subscriptionId", + "name": "apiVersion", "value": "string", - "description": "The ID of the subscription to cancel." - }, - { - "filePath": "src/server/authenticate/admin/billing/types.ts", - "syntaxKind": "PropertySignature", - "name": "prorate", - "value": "boolean", - "description": "Whether to prorate the cancellation.\n\n\n\n\n", - "isOptional": true - }, - { - "filePath": "src/server/authenticate/admin/billing/types.ts", - "syntaxKind": "PropertySignature", - "name": "isTest", - "value": "boolean", - "description": "", - "isOptional": true - } - ], - "value": "export interface CancelBillingOptions {\n /**\n * The ID of the subscription to cancel.\n */\n subscriptionId: string;\n /**\n * Whether to prorate the cancellation.\n *\n * {@link https://shopify.dev/docs/apps/billing/subscriptions/cancel-recurring-charges}\n */\n prorate?: boolean;\n /*\n * Whether to use the test mode. This prevents the credit card from being charged. Test shops and demo shops cannot be charged.\n */\n isTest?: boolean;\n}" - }, - "EnsureCORSFunction": { - "filePath": "src/server/authenticate/helpers/ensure-cors-headers.ts", - "name": "EnsureCORSFunction", - "description": "", - "members": [], - "value": "export interface EnsureCORSFunction {\n (response: Response): Response;\n}" - }, - "EmbeddedAdminContext": { - "filePath": "src/server/authenticate/admin/types.ts", - "name": "EmbeddedAdminContext", - "description": "", - "members": [ - { - "filePath": "src/server/authenticate/admin/types.ts", - "syntaxKind": "PropertySignature", - "name": "sessionToken", - "value": "JwtPayload", - "description": "The decoded and validated session token for the request.\n\nReturned only if `isEmbeddedApp` is `true`.\n\n\n\n\n", + "description": "The API version used for the webhook.", "examples": [ { - "title": "Using the decoded session token", - "description": "Get user-specific data using the `sessionToken` object.", + "title": "Webhook API version", + "description": "Get the API version used for webhook request.", "tabs": [ { - "code": "import { shopifyApp } from \"@shopify/shopify-app-remix/server\";\n\nconst shopify = shopifyApp({\n // ...etc\n useOnlineTokens: true,\n});\nexport default shopify;\nexport const authenticate = shopify.authenticate;", - "title": "shopify.server.ts" - }, - { - "code": "import { LoaderFunctionArgs, json } from \"@remix-run/node\";\nimport { authenticate } from \"../shopify.server\";\nimport { getMyAppData } from \"~/db/model.server\";\n\nexport const loader = async ({ request }: LoaderFunctionArgs) => {\n const { sessionToken } = await authenticate.public.checkout(\n request\n );\n return json(await getMyAppData({user: sessionToken.sub}));\n};", - "title": "/app/routes/**\\/*.ts" + "code": "import { ActionFunctionArgs } from \"@remix-run/node\";\nimport { authenticate } from \"../shopify.server\";\n\nexport const action = async ({ request }: ActionFunctionArgs) => {\n const { apiVersion } = await authenticate.webhook(request);\n return new Response();\n};", + "title": "/app/routes/webhooks.tsx" } ] } ] }, { - "filePath": "src/server/authenticate/admin/types.ts", + "filePath": "src/server/authenticate/webhooks/types.ts", "syntaxKind": "PropertySignature", - "name": "redirect", - "value": "RedirectFunction", - "description": "A function that redirects the user to a new page, ensuring that the appropriate parameters are set for embedded apps.\n\nReturned only if `isEmbeddedApp` is `true`.", + "name": "shop", + "value": "string", + "description": "The shop where the webhook was triggered.", "examples": [ { - "title": "Redirecting to an app route", - "description": "Use the `redirect` helper to safely redirect between pages.", + "title": "Webhook shop", + "description": "Get the shop that triggered a webhook.", "tabs": [ { - "code": "import { LoaderFunctionArgs, json } from \"@remix-run/node\";\nimport { authenticate } from \"../shopify.server\";\n\nexport const loader = async ({ request }: LoaderFunctionArgs) => {\n const { session, redirect } = await authenticate.admin(request);\n return redirect(\"/\");\n};", - "title": "/app/routes/admin/my-route.ts" + "code": "import { ActionFunctionArgs } from \"@remix-run/node\";\nimport { authenticate } from \"../shopify.server\";\n\nexport const action = async ({ request }: ActionFunctionArgs) => {\n const { shop } = await authenticate.webhook(request);\n return new Response();\n};", + "title": "/app/routes/webhooks.tsx" } ] - }, + } + ] + }, + { + "filePath": "src/server/authenticate/webhooks/types.ts", + "syntaxKind": "PropertySignature", + "name": "topic", + "value": "Topics", + "description": "The topic of the webhook.", + "examples": [ { - "title": "Redirecting outside of Shopify admin", - "description": "Pass in a `target` option of `_top` or `_parent` to go to an external URL.", + "title": "Webhook topic", + "description": "Get the event topic for the webhook.", "tabs": [ { - "code": "import { LoaderFunctionArgs, json } from \"@remix-run/node\";\nimport { authenticate } from \"../shopify.server\";\n\nexport const loader = async ({ request }: LoaderFunctionArgs) => {\n const { session, redirect } = await authenticate.admin(request);\n return redirect(\"/\", { target: '_parent' });\n};", - "title": "/app/routes/admin/my-route.ts" + "code": "import { ActionFunctionArgs } from \"@remix-run/node\";\nimport { authenticate } from \"../shopify.server\";\n\nexport const action = async ({ request }: ActionFunctionArgs) => {\n const { topic } = await authenticate.webhook(request);\n\n switch (topic) {\n case \"APP_UNINSTALLED\":\n // Do something when the app is uninstalled.\n break;\n }\n\n return new Response();\n};", + "title": "/app/routes/webhooks.tsx" } ] } ] }, { - "filePath": "src/server/authenticate/admin/types.ts", + "filePath": "src/server/authenticate/webhooks/types.ts", "syntaxKind": "PropertySignature", - "name": "session", - "value": "Session", - "description": "The session for the user who made the request.\n\nThis comes from the session storage which `shopifyApp` uses to store sessions in your database of choice.\n\nUse this to get shop or user-specific data.", + "name": "webhookId", + "value": "string", + "description": "A unique ID for the webhook. Useful to keep track of which events your app has already processed.", "examples": [ { - "title": "Using offline sessions", - "description": "Get your app's shop-specific data using an offline session.", + "title": "Webhook ID", + "description": "Get the webhook ID.", "tabs": [ { - "code": "import { shopifyApp } from \"@shopify/shopify-app-remix/server\";\n\nconst shopify = shopifyApp({\n // ...etc\n});\nexport default shopify;\nexport const authenticate = shopify.authenticate;", - "title": "shopify.server.ts" - }, - { - "code": "import { LoaderFunctionArgs, json } from \"@remix-run/node\";\nimport { authenticate } from \"../shopify.server\";\nimport { getMyAppData } from \"~/db/model.server\";\n\nexport const loader = async ({ request }: LoaderFunctionArgs) => {\n const { session } = await authenticate.admin(request);\n return json(await getMyAppData({shop: session.shop));\n};", - "title": "/app/routes/**\\/*.ts" + "code": "import { ActionFunctionArgs } from \"@remix-run/node\";\nimport { authenticate } from \"../shopify.server\";\n\nexport const action = async ({ request }: ActionFunctionArgs) => {\n const { webhookId } = await authenticate.webhook(request);\n return new Response();\n};", + "title": "/app/routes/webhooks.tsx" } ] - }, + } + ] + }, + { + "filePath": "src/server/authenticate/webhooks/types.ts", + "syntaxKind": "PropertySignature", + "name": "payload", + "value": "JSONValue", + "description": "The payload from the webhook request.", + "examples": [ { - "title": "Using online sessions", - "description": "Get your app's user-specific data using an online session.", + "title": "Webhook payload", + "description": "Get the request's POST payload.", "tabs": [ { - "code": "import { shopifyApp } from \"@shopify/shopify-app-remix/server\";\n\nconst shopify = shopifyApp({\n // ...etc\n useOnlineTokens: true,\n});\nexport default shopify;\nexport const authenticate = shopify.authenticate;", - "title": "shopify.server.ts" - }, - { - "code": "import { LoaderFunctionArgs, json } from \"@remix-run/node\";\nimport { authenticate } from \"../shopify.server\";\nimport { getMyAppData } from \"~/db/model.server\";\n\nexport const loader = async ({ request }: LoaderFunctionArgs) => {\n const { session } = await authenticate.admin(request);\n return json(await getMyAppData({user: session.onlineAccessInfo!.id}));\n};", - "title": "/app/routes/**\\/*.ts" + "code": "import { ActionFunctionArgs } from \"@remix-run/node\";\nimport { authenticate } from \"../shopify.server\";\n\nexport const action = async ({ request }: ActionFunctionArgs) => {\n const { payload } = await authenticate.webhook(request);\n return new Response();\n};", + "title": "/app/routes/webhooks.tsx" } ] } ] }, { - "filePath": "src/server/authenticate/admin/types.ts", - "syntaxKind": "PropertySignature", - "name": "admin", - "value": "AdminApiContext", - "description": "Methods for interacting with the GraphQL / REST Admin APIs for the store that made the request." - }, - { - "filePath": "src/server/authenticate/admin/types.ts", - "syntaxKind": "PropertySignature", - "name": "billing", - "value": "BillingContext", - "description": "Billing methods for this store, based on the plans defined in the `billing` config option.\n\n\n\n\n" - }, - { - "filePath": "src/server/authenticate/admin/types.ts", + "filePath": "src/server/authenticate/webhooks/types.ts", "syntaxKind": "PropertySignature", - "name": "cors", - "value": "EnsureCORSFunction", - "description": "A function that ensures the CORS headers are set correctly for the response.", + "name": "subTopic", + "value": "string", + "description": "The sub-topic of the webhook. This is only available for certain webhooks.", + "isOptional": true, "examples": [ { - "title": "Setting CORS headers for a admin request", - "description": "Use the `cors` helper to ensure your app can respond to requests from admin extensions.", + "title": "Webhook sub-topic", + "description": "Get the webhook sub-topic.", "tabs": [ { - "code": "import { LoaderFunctionArgs, json } from \"@remix-run/node\";\nimport { authenticate } from \"../shopify.server\";\nimport { getMyAppData } from \"~/db/model.server\";\n\nexport const loader = async ({ request }: LoaderFunctionArgs) => {\n const { session, cors } = await authenticate.admin(request);\n return cors(json(await getMyAppData({user: session.onlineAccessInfo!.id})));\n};", - "title": "/app/routes/admin/my-route.ts" + "code": "import { ActionFunctionArgs } from \"@remix-run/node\";\nimport { authenticate } from \"../shopify.server\";\n\nexport const action = async ({ request }: ActionFunctionArgs) => {\n const { subTopic } = await authenticate.webhook(request);\n return new Response();\n};", + "title": "/app/routes/webhooks.tsx" } ] } ] } ], - "value": "export interface EmbeddedAdminContext<\n Config extends AppConfigArg,\n Resources extends ShopifyRestResources = ShopifyRestResources,\n> extends AdminContextInternal {\n /**\n * The decoded and validated session token for the request.\n *\n * Returned only if `isEmbeddedApp` is `true`.\n *\n * {@link https://shopify.dev/docs/apps/auth/oauth/session-tokens#payload}\n *\n * @example\n * Using the decoded session token.\n * Get user-specific data using the `sessionToken` object.\n * ```ts\n * // shopify.server.ts\n * import { shopifyApp } from \"@shopify/shopify-app-remix/server\";\n *\n * const shopify = shopifyApp({\n * // ...etc\n * useOnlineTokens: true,\n * });\n * export default shopify;\n * export const authenticate = shopify.authenticate;\n * ```\n * ```ts\n * // /app/routes/**\\/*.ts\n * import { LoaderFunctionArgs, json } from \"@remix-run/node\";\n * import { authenticate } from \"../shopify.server\";\n * import { getMyAppData } from \"~/db/model.server\";\n *\n * export const loader = async ({ request }: LoaderFunctionArgs) => {\n * const { sessionToken } = await authenticate.public.checkout(\n * request\n * );\n * return json(await getMyAppData({user: sessionToken.sub}));\n * };\n * ```\n */\n sessionToken: JwtPayload;\n\n /**\n * A function that redirects the user to a new page, ensuring that the appropriate parameters are set for embedded\n * apps.\n *\n * Returned only if `isEmbeddedApp` is `true`.\n *\n * @example\n * Redirecting to an app route.\n * Use the `redirect` helper to safely redirect between pages.\n * ```ts\n * // /app/routes/admin/my-route.ts\n * import { LoaderFunctionArgs, json } from \"@remix-run/node\";\n * import { authenticate } from \"../shopify.server\";\n *\n * export const loader = async ({ request }: LoaderFunctionArgs) => {\n * const { session, redirect } = await authenticate.admin(request);\n * return redirect(\"/\");\n * };\n * ```\n *\n * @example\n * Redirecting outside of Shopify admin.\n * Pass in a `target` option of `_top` or `_parent` to go to an external URL.\n * ```ts\n * // /app/routes/admin/my-route.ts\n * import { LoaderFunctionArgs, json } from \"@remix-run/node\";\n * import { authenticate } from \"../shopify.server\";\n *\n * export const loader = async ({ request }: LoaderFunctionArgs) => {\n * const { session, redirect } = await authenticate.admin(request);\n * return redirect(\"/\", { target: '_parent' });\n * };\n * ```\n */\n redirect: RedirectFunction;\n}" - }, - "JwtPayload": { - "filePath": "../../node_modules/@shopify/shopify-api/lib/session/types.d.ts", - "name": "JwtPayload", - "description": "", - "members": [ - { - "filePath": "../../node_modules/@shopify/shopify-api/lib/session/types.d.ts", - "syntaxKind": "PropertySignature", - "name": "iss", - "value": "string", - "description": "The shop's admin domain." - }, - { - "filePath": "../../node_modules/@shopify/shopify-api/lib/session/types.d.ts", - "syntaxKind": "PropertySignature", - "name": "dest", - "value": "string", - "description": "The shop's domain." - }, - { - "filePath": "../../node_modules/@shopify/shopify-api/lib/session/types.d.ts", - "syntaxKind": "PropertySignature", - "name": "aud", - "value": "string", - "description": "The client ID of the receiving app." - }, - { - "filePath": "../../node_modules/@shopify/shopify-api/lib/session/types.d.ts", - "syntaxKind": "PropertySignature", - "name": "sub", - "value": "string", - "description": "The User that the session token is intended for." - }, - { - "filePath": "../../node_modules/@shopify/shopify-api/lib/session/types.d.ts", - "syntaxKind": "PropertySignature", - "name": "exp", - "value": "number", - "description": "When the session token expires." - }, - { - "filePath": "../../node_modules/@shopify/shopify-api/lib/session/types.d.ts", - "syntaxKind": "PropertySignature", - "name": "nbf", - "value": "number", - "description": "When the session token activates." - }, - { - "filePath": "../../node_modules/@shopify/shopify-api/lib/session/types.d.ts", - "syntaxKind": "PropertySignature", - "name": "iat", - "value": "number", - "description": "When the session token was issued." - }, - { - "filePath": "../../node_modules/@shopify/shopify-api/lib/session/types.d.ts", - "syntaxKind": "PropertySignature", - "name": "jti", - "value": "string", - "description": "A secure random UUID." - }, - { - "filePath": "../../node_modules/@shopify/shopify-api/lib/session/types.d.ts", - "syntaxKind": "PropertySignature", - "name": "sid", - "value": "string", - "description": "A unique session ID per user and app." - } - ], - "value": "export interface JwtPayload {\n /**\n * The shop's admin domain.\n */\n iss: string;\n /**\n * The shop's domain.\n */\n dest: string;\n /**\n * The client ID of the receiving app.\n */\n aud: string;\n /**\n * The User that the session token is intended for.\n */\n sub: string;\n /**\n * When the session token expires.\n */\n exp: number;\n /**\n * When the session token activates.\n */\n nbf: number;\n /**\n * When the session token was issued.\n */\n iat: number;\n /**\n * A secure random UUID.\n */\n jti: string;\n /**\n * A unique session ID per user and app.\n */\n sid: string;\n}" - }, - "RedirectFunction": { - "filePath": "src/server/authenticate/admin/helpers/redirect.ts", - "name": "RedirectFunction", - "description": "", - "params": [ - { - "name": "url", - "description": "", - "value": "string", - "filePath": "src/server/authenticate/admin/helpers/redirect.ts" - }, - { - "name": "init", - "description": "", - "value": "RedirectInit", - "isOptional": true, - "filePath": "src/server/authenticate/admin/helpers/redirect.ts" - } - ], - "returns": { - "filePath": "src/server/authenticate/admin/helpers/redirect.ts", - "description": "", - "name": "TypedResponse", - "value": "TypedResponse" - }, - "value": "export type RedirectFunction = (\n url: string,\n init?: RedirectInit,\n) => TypedResponse;" - }, - "RedirectInit": { - "filePath": "src/server/authenticate/admin/helpers/redirect.ts", - "syntaxKind": "TypeAliasDeclaration", - "name": "RedirectInit", - "value": "number | (ResponseInit & {target?: RedirectTarget})", - "description": "" - }, - "RedirectTarget": { - "filePath": "src/server/authenticate/admin/helpers/redirect.ts", - "syntaxKind": "TypeAliasDeclaration", - "name": "RedirectTarget", - "value": "'_self' | '_parent' | '_top'", - "description": "" - }, - "RestResourcesType": { - "filePath": "src/server/types.ts", - "syntaxKind": "TypeAliasDeclaration", - "name": "RestResourcesType", - "value": "Config['restResources'] extends ShopifyRestResources\n ? Config['restResources']\n : ShopifyRestResources", - "description": "" - }, - "AuthenticateFlow": { - "filePath": "src/server/authenticate/flow/types.ts", - "name": "AuthenticateFlow", - "description": "", - "params": [ - { - "name": "request", - "description": "", - "value": "Request", - "filePath": "src/server/authenticate/flow/types.ts" - } - ], - "returns": { - "filePath": "src/server/authenticate/flow/types.ts", - "description": "", - "name": "Promise>", - "value": "Promise>" - }, - "value": "export type AuthenticateFlow<\n Resources extends ShopifyRestResources = ShopifyRestResources,\n> = (request: Request) => Promise>;" - }, - "FlowContext": { - "filePath": "src/server/authenticate/flow/types.ts", - "name": "FlowContext", - "description": "", - "members": [ - { - "filePath": "src/server/authenticate/flow/types.ts", - "syntaxKind": "PropertySignature", - "name": "session", - "value": "Session", - "description": "A session with an offline token for the shop.\n\nReturned only if there is a session for the shop.", - "examples": [ - { - "title": "Shopify session for the Flow request", - "description": "Use the session associated with this request to use REST resources.", - "tabs": [ - { - "code": "import { ActionFunction } from \"@remix-run/node\";\nimport { authenticate } from \"../shopify.server\";\n\nexport const action: ActionFunction = async ({ request }) => {\n const { session, admin } = await authenticate.flow(request);\n\n const products = await admin?.rest.resources.Product.all({ session });\n // Use products\n\n return new Response();\n};", - "title": "/app/routes/flow.tsx" - } - ] - } - ] - }, - { - "filePath": "src/server/authenticate/flow/types.ts", - "syntaxKind": "PropertySignature", - "name": "payload", - "value": "any", - "description": "The payload from the Flow request.", - "examples": [ - { - "title": "Flow payload", - "description": "Get the request's POST payload.", - "tabs": [ - { - "code": "import { ActionFunction } from \"@remix-run/node\";\nimport { authenticate } from \"../shopify.server\";\n\nexport const action: ActionFunction = async ({ request }) => {\n const { payload } = await authenticate.flow(request);\n return new Response();\n};", - "title": "/app/routes/flow.tsx" - } - ] - } - ] - }, - { - "filePath": "src/server/authenticate/flow/types.ts", - "syntaxKind": "PropertySignature", - "name": "admin", - "value": "AdminApiContext", - "description": "An admin context for the Flow request.\n\nReturned only if there is a session for the shop.", - "examples": [ - { - "title": "Flow admin context", - "description": "Use the `admin` object in the context to interact with the Admin API.", - "tabs": [ - { - "code": "import { ActionFunctionArgs } from \"@remix-run/node\";\nimport { authenticate } from \"../shopify.server\";\n\nexport async function action({ request }: ActionFunctionArgs) {\n const { admin } = await authenticate.flow(request);\n\n const response = await admin?.graphql(\n `#graphql\n mutation populateProduct($input: ProductInput!) {\n productCreate(input: $input) {\n product {\n id\n }\n }\n }`,\n { variables: { input: { title: \"Product Name\" } } }\n );\n\n const productData = await response.json();\n return json({ data: productData.data });\n}", - "title": "/app/routes/flow.tsx" - } - ] - } - ] - } - ], - "value": "export interface FlowContext<\n Resources extends ShopifyRestResources = ShopifyRestResources,\n> {\n /**\n * A session with an offline token for the shop.\n *\n * Returned only if there is a session for the shop.\n *\n * @example\n * Shopify session for the Flow request.\n * Use the session associated with this request to use REST resources.\n * ```ts\n * // /app/routes/flow.tsx\n * import { ActionFunction } from \"@remix-run/node\";\n * import { authenticate } from \"../shopify.server\";\n *\n * export const action: ActionFunction = async ({ request }) => {\n * const { session, admin } = await authenticate.flow(request);\n *\n * const products = await admin?.rest.resources.Product.all({ session });\n * // Use products\n *\n * return new Response();\n * };\n * ```\n */\n session: Session;\n\n /**\n * The payload from the Flow request.\n *\n * @example\n * Flow payload.\n * Get the request's POST payload.\n * ```ts\n * // /app/routes/flow.tsx\n * import { ActionFunction } from \"@remix-run/node\";\n * import { authenticate } from \"../shopify.server\";\n *\n * export const action: ActionFunction = async ({ request }) => {\n * const { payload } = await authenticate.flow(request);\n * return new Response();\n * };\n * ```\n */\n payload: any;\n\n /**\n * An admin context for the Flow request.\n *\n * Returned only if there is a session for the shop.\n *\n * @example\n * Flow admin context.\n * Use the `admin` object in the context to interact with the Admin API.\n * ```ts\n * // /app/routes/flow.tsx\n * import { ActionFunctionArgs } from \"@remix-run/node\";\n * import { authenticate } from \"../shopify.server\";\n *\n * export async function action({ request }: ActionFunctionArgs) {\n * const { admin } = await authenticate.flow(request);\n *\n * const response = await admin?.graphql(\n * `#graphql\n * mutation populateProduct($input: ProductInput!) {\n * productCreate(input: $input) {\n * product {\n * id\n * }\n * }\n * }`,\n * { variables: { input: { title: \"Product Name\" } } }\n * );\n *\n * const productData = await response.json();\n * return json({ data: productData.data });\n * }\n * ```\n */\n admin: AdminApiContext;\n}" - }, - "AuthenticatePublic": { - "filePath": "src/server/authenticate/public/types.ts", - "syntaxKind": "TypeAliasDeclaration", - "name": "AuthenticatePublic", - "value": "FeatureEnabled extends true\n ? AuthenticatePublicObject\n : AuthenticatePublicLegacy", - "description": "" - }, - "AuthenticatePublicObject": { - "filePath": "src/server/authenticate/public/types.ts", - "name": "AuthenticatePublicObject", - "description": "", - "members": [ - { - "filePath": "src/server/authenticate/public/types.ts", - "syntaxKind": "PropertySignature", - "name": "checkout", - "value": "AuthenticateCheckout", - "description": "Authenticate a request from a checkout extension", - "examples": [ - { - "title": "Authenticating a checkout extension request", - "description": "", - "tabs": [ - { - "code": "import { LoaderFunctionArgs, json } from \"@remix-run/node\";\nimport { authenticate } from \"../shopify.server\";\n\nexport const loader = async ({ request }: LoaderFunctionArgs) => {\n const { sessionToken, cors } = await authenticate.public.checkout(\n request,\n );\n return cors(json({my: \"data\", shop: sessionToken.dest}));\n};", - "title": "/app/routes/public/widgets.ts" - } - ] - } - ] - }, - { - "filePath": "src/server/authenticate/public/types.ts", - "syntaxKind": "PropertySignature", - "name": "appProxy", - "value": "AuthenticateAppProxy", - "description": "Authenticate a request from an app proxy", - "examples": [ - { - "title": "Authenticating an app proxy request", - "description": "", - "tabs": [ - { - "code": "import { LoaderFunctionArgs, json } from \"@remix-run/node\";\nimport { authenticate } from \"../shopify.server\";\n\nexport const loader = async ({ request }: LoaderFunctionArgs) => {\n await authenticate.public.appProxy(\n request,\n );\n\n const {searchParams} = new URL(request.url);\n const shop = searchParams.get(\"shop\");\n const customerId = searchParams.get(\"logged_in_customer_id\")\n\n return json({my: \"data\", shop, customerId});\n};", - "title": "/app/routes/public/widgets.ts" - } - ] - } - ] - } - ], - "value": "export interface AuthenticatePublicObject {\n /**\n * Authenticate a request from a checkout extension\n *\n * @example\n * Authenticating a checkout extension request\n * ```ts\n * // /app/routes/public/widgets.ts\n * import { LoaderFunctionArgs, json } from \"@remix-run/node\";\n * import { authenticate } from \"../shopify.server\";\n *\n * export const loader = async ({ request }: LoaderFunctionArgs) => {\n * const { sessionToken, cors } = await authenticate.public.checkout(\n * request,\n * );\n * return cors(json({my: \"data\", shop: sessionToken.dest}));\n * };\n * ```\n */\n checkout: AuthenticateCheckout;\n\n /**\n * Authenticate a request from an app proxy\n *\n * @example\n * Authenticating an app proxy request\n * ```ts\n * // /app/routes/public/widgets.ts\n * import { LoaderFunctionArgs, json } from \"@remix-run/node\";\n * import { authenticate } from \"../shopify.server\";\n *\n * export const loader = async ({ request }: LoaderFunctionArgs) => {\n * await authenticate.public.appProxy(\n * request,\n * );\n *\n * const {searchParams} = new URL(request.url);\n * const shop = searchParams.get(\"shop\");\n * const customerId = searchParams.get(\"logged_in_customer_id\")\n *\n * return json({my: \"data\", shop, customerId});\n * };\n * ```\n */\n appProxy: AuthenticateAppProxy;\n}" - }, - "AuthenticateCheckout": { - "filePath": "src/server/authenticate/public/checkout/types.ts", - "name": "AuthenticateCheckout", - "description": "", - "params": [ - { - "name": "request", - "description": "", - "value": "Request", - "filePath": "src/server/authenticate/public/checkout/types.ts" - }, - { - "name": "options", - "description": "", - "value": "AuthenticateCheckoutOptions", - "isOptional": true, - "filePath": "src/server/authenticate/public/checkout/types.ts" - } - ], - "returns": { - "filePath": "src/server/authenticate/public/checkout/types.ts", - "description": "", - "name": "Promise", - "value": "Promise" - }, - "value": "export type AuthenticateCheckout = (\n request: Request,\n options?: AuthenticateCheckoutOptions,\n) => Promise;" - }, - "AuthenticateCheckoutOptions": { - "filePath": "src/server/authenticate/public/checkout/types.ts", - "name": "AuthenticateCheckoutOptions", - "description": "", - "members": [ - { - "filePath": "src/server/authenticate/public/checkout/types.ts", - "syntaxKind": "PropertySignature", - "name": "corsHeaders", - "value": "string[]", - "description": "", - "isOptional": true - } - ], - "value": "export interface AuthenticateCheckoutOptions {\n corsHeaders?: string[];\n}" - }, - "CheckoutContext": { - "filePath": "src/server/authenticate/public/checkout/types.ts", - "name": "CheckoutContext", - "description": "Authenticated Context for a checkout request", - "members": [ - { - "filePath": "src/server/authenticate/public/checkout/types.ts", - "syntaxKind": "PropertySignature", - "name": "sessionToken", - "value": "JwtPayload", - "description": "The decoded and validated session token for the request\n\nRefer to the OAuth docs for the [session token payload](https://shopify.dev/docs/apps/auth/oauth/session-tokens#payload).", - "examples": [ - { - "title": "Using the decoded session token", - "description": "Get store-specific data using the `sessionToken` object.", - "tabs": [ - { - "code": "import { LoaderFunctionArgs, json } from \"@remix-run/node\";\nimport { authenticate } from \"../shopify.server\";\nimport { getMyAppData } from \"~/db/model.server\";\n\nexport const loader = async ({ request }: LoaderFunctionArgs) => {\n const { sessionToken } = await authenticate.public.checkout(\n request\n );\n return json(await getMyAppData({shop: sessionToken.dest}));\n};", - "title": "app/routes/public/my-route.ts" - } - ] - } - ] - }, - { - "filePath": "src/server/authenticate/public/checkout/types.ts", - "syntaxKind": "PropertySignature", - "name": "cors", - "value": "EnsureCORSFunction", - "description": "A function that ensures the CORS headers are set correctly for the response.", - "examples": [ - { - "title": "Setting CORS headers for a public request", - "description": "Use the `cors` helper to ensure your app can respond to checkout extension requests.", - "tabs": [ - { - "code": "import { LoaderFunctionArgs, json } from \"@remix-run/node\";\nimport { authenticate } from \"../shopify.server\";\nimport { getMyAppData } from \"~/db/model.server\";\n\nexport const loader = async ({ request }: LoaderFunctionArgs) => {\n const { sessionToken, cors } = await authenticate.public.checkout(\n request,\n { corsHeaders: [\"X-My-Custom-Header\"] }\n );\n const data = await getMyAppData({shop: sessionToken.dest});\n return cors(json(data));\n};", - "title": "app/routes/public/my-route.ts" - } - ] - } - ] - } - ], - "value": "export interface CheckoutContext {\n /**\n * The decoded and validated session token for the request\n *\n * Refer to the OAuth docs for the [session token payload](https://shopify.dev/docs/apps/auth/oauth/session-tokens#payload).\n *\n * @example\n * Using the decoded session token.\n * Get store-specific data using the `sessionToken` object.\n * ```ts\n * // app/routes/public/my-route.ts\n * import { LoaderFunctionArgs, json } from \"@remix-run/node\";\n * import { authenticate } from \"../shopify.server\";\n * import { getMyAppData } from \"~/db/model.server\";\n *\n * export const loader = async ({ request }: LoaderFunctionArgs) => {\n * const { sessionToken } = await authenticate.public.checkout(\n * request\n * );\n * return json(await getMyAppData({shop: sessionToken.dest}));\n * };\n * ```\n */\n sessionToken: JwtPayload;\n\n /**\n * A function that ensures the CORS headers are set correctly for the response.\n *\n * @example\n * Setting CORS headers for a public request.\n * Use the `cors` helper to ensure your app can respond to checkout extension requests.\n * ```ts\n * // app/routes/public/my-route.ts\n * import { LoaderFunctionArgs, json } from \"@remix-run/node\";\n * import { authenticate } from \"../shopify.server\";\n * import { getMyAppData } from \"~/db/model.server\";\n *\n * export const loader = async ({ request }: LoaderFunctionArgs) => {\n * const { sessionToken, cors } = await authenticate.public.checkout(\n * request,\n * { corsHeaders: [\"X-My-Custom-Header\"] }\n * );\n * const data = await getMyAppData({shop: sessionToken.dest});\n * return cors(json(data));\n * };\n * ```\n */\n cors: EnsureCORSFunction;\n}" - }, - "AuthenticateAppProxy": { - "filePath": "src/server/authenticate/public/appProxy/types.ts", - "name": "AuthenticateAppProxy", - "description": "", - "params": [ - { - "name": "request", - "description": "", - "value": "Request", - "filePath": "src/server/authenticate/public/appProxy/types.ts" - } - ], - "returns": { - "filePath": "src/server/authenticate/public/appProxy/types.ts", - "description": "", - "name": "Promise", - "value": "Promise" - }, - "value": "export type AuthenticateAppProxy = (\n request: Request,\n) => Promise;" - }, - "AppProxyContext": { - "filePath": "src/server/authenticate/public/appProxy/types.ts", - "name": "AppProxyContext", - "description": "", - "members": [ - { - "filePath": "src/server/authenticate/public/appProxy/types.ts", - "syntaxKind": "PropertySignature", - "name": "session", - "value": "undefined", - "description": "No session is available for the shop that made this request.\n\nThis comes from the session storage which `shopifyApp` uses to store sessions in your database of choice." - }, - { - "filePath": "src/server/authenticate/public/appProxy/types.ts", - "syntaxKind": "PropertySignature", - "name": "admin", - "value": "undefined", - "description": "No session is available for the shop that made this request. Therefore no methods for interacting with the GraphQL / REST Admin APIs are available." - }, - { - "filePath": "src/server/authenticate/public/appProxy/types.ts", - "syntaxKind": "PropertySignature", - "name": "storefront", - "value": "undefined", - "description": "No session is available for the shop that made this request. Therefore no method for interacting with the Storefront API is available." - }, - { - "filePath": "src/server/authenticate/public/appProxy/types.ts", - "syntaxKind": "PropertySignature", - "name": "liquid", - "value": "LiquidResponseFunction", - "description": "A utility for creating a Liquid Response.", - "examples": [ - { - "title": "Rendering liquid content", - "description": "Use the `liquid` helper to render a `Response` with Liquid content using the shop's theme.", - "tabs": [ - { - "code": "import {authenticate} from \"~/shopify.server\"\n\nexport async function loader({ request }) {\n const {liquid} = await authenticate.public.appProxy(request);\n\n return liquid(\"Hello {{shop.name}}\");\n}", - "title": "app/routes/**\\/.ts" - } - ] - }, - { - "title": "Rendering liquid content without a layout", - "description": "Set the `layout` option to `false` to render the Liquid content without a theme.", - "tabs": [ - { - "code": "import {authenticate} from \"~/shopify.server\"\n\nexport async function loader({ request }) {\n const {liquid} = await authenticate.public.appProxy(request);\n\n return liquid(\n \"Hello {{shop.name}}\",\n { layout: false }\n );\n}", - "title": "app/routes/**\\/.ts" - } - ] - } - ] - } - ], - "value": "export interface AppProxyContext extends Context {\n /**\n * No session is available for the shop that made this request.\n *\n * This comes from the session storage which `shopifyApp` uses to store sessions in your database of choice.\n */\n session: undefined;\n\n /**\n * No session is available for the shop that made this request.\n * Therefore no methods for interacting with the GraphQL / REST Admin APIs are available.\n */\n admin: undefined;\n\n /**\n * No session is available for the shop that made this request.\n * Therefore no method for interacting with the Storefront API is available.\n */\n storefront: undefined;\n}" - }, - "LiquidResponseFunction": { - "filePath": "src/server/authenticate/public/appProxy/types.ts", - "name": "LiquidResponseFunction", - "description": "", - "params": [ - { - "name": "body", - "description": "", - "value": "string", - "filePath": "src/server/authenticate/public/appProxy/types.ts" - }, - { - "name": "initAndOptions", - "description": "", - "value": "number | (ResponseInit & Options)", - "isOptional": true, - "filePath": "src/server/authenticate/public/appProxy/types.ts" - } - ], - "returns": { - "filePath": "src/server/authenticate/public/appProxy/types.ts", - "description": "", - "name": "Response", - "value": "Response" - }, - "value": "export type LiquidResponseFunction = (\n body: string,\n initAndOptions?: number | (ResponseInit & Options),\n) => Response;" - }, - "Options": { - "filePath": "src/server/authenticate/public/appProxy/types.ts", - "name": "Options", - "description": "", - "members": [ - { - "filePath": "src/server/authenticate/public/appProxy/types.ts", - "syntaxKind": "PropertySignature", - "name": "layout", - "value": "boolean", - "description": "Whether to use the shop's theme layout around the Liquid content.", - "isOptional": true - } - ], - "value": "interface Options {\n /**\n * Whether to use the shop's theme layout around the Liquid content.\n */\n layout?: boolean;\n}" - }, - "AppProxyContextWithSession": { - "filePath": "src/server/authenticate/public/appProxy/types.ts", - "name": "AppProxyContextWithSession", - "description": "", - "members": [ - { - "filePath": "src/server/authenticate/public/appProxy/types.ts", - "syntaxKind": "PropertySignature", - "name": "session", - "value": "Session", - "description": "The session for the shop that made the request.\n\nThis comes from the session storage which `shopifyApp` uses to store sessions in your database of choice.\n\nUse this to get shop or user-specific data.", - "examples": [ - { - "title": "Using the session object", - "description": "Get the session for the shop that initiated the request to the app proxy.", - "tabs": [ - { - "code": "import { json } from \"@remix-run/node\";\nimport { authenticate } from \"../shopify.server\";\nimport { getMyAppModelData } from \"~/db/model.server\";\n\nexport const loader = async ({ request }) => {\n // Get the session for the shop that initiated the request to the app proxy.\n const { session } = await authenticate.public.appProxy(request);\n\n // Use the session data to make to queries to your database or additional requests.\n return json(await getMyAppModelData({shop: session.shop));\n};", - "title": "app/routes/**\\/.ts" - } - ] - } - ] - }, - { - "filePath": "src/server/authenticate/public/appProxy/types.ts", - "syntaxKind": "PropertySignature", - "name": "admin", - "value": "AdminApiContext", - "description": "Methods for interacting with the GraphQL / REST Admin APIs for the store that made the request.", - "examples": [ - { - "title": "Interacting with the Admin API", - "description": "Use the `admin` object to interact with the REST or GraphQL APIs.", - "tabs": [ - { - "code": "import { json } from \"@remix-run/node\";\nimport { authenticate } from \"../shopify.server\";\n\nexport async function action({ request }: ActionFunctionArgs) {\n const { admin } = await authenticate.public.appProxy(request);\n\n const response = await admin.graphql(\n `#graphql\n mutation populateProduct($input: ProductInput!) {\n productCreate(input: $input) {\n product {\n id\n }\n }\n }`,\n { variables: { input: { title: \"Product Name\" } } }\n );\n\n const productData = await response.json();\n return json({ data: productData.data });\n}", - "title": "app/routes/**\\/.ts" - } - ] - } - ] - }, - { - "filePath": "src/server/authenticate/public/appProxy/types.ts", - "syntaxKind": "PropertySignature", - "name": "storefront", - "value": "StorefrontContext", - "description": "Method for interacting with the Shopify Storefront Graphql API for the store that made the request.", - "examples": [ - { - "title": "Interacting with the Storefront API", - "description": "Use the `storefront` object to interact with the GraphQL API.", - "tabs": [ - { - "code": "import { json } from \"@remix-run/node\";\nimport { authenticate } from \"../shopify.server\";\n\nexport async function action({ request }: ActionFunctionArgs) {\n const { storefront } = await authenticate.public.appProxy(request);\n\n const response = await storefront.graphql(`{blogs(first: 10) { edges { node { id } } } }`);\n\n return json(await response.json());\n}", - "title": "app/routes/**\\/.ts" - } - ] - } - ] - }, - { - "filePath": "src/server/authenticate/public/appProxy/types.ts", - "syntaxKind": "PropertySignature", - "name": "liquid", - "value": "LiquidResponseFunction", - "description": "A utility for creating a Liquid Response.", - "examples": [ - { - "title": "Rendering liquid content", - "description": "Use the `liquid` helper to render a `Response` with Liquid content using the shop's theme.", - "tabs": [ - { - "code": "import {authenticate} from \"~/shopify.server\"\n\nexport async function loader({ request }) {\n const {liquid} = await authenticate.public.appProxy(request);\n\n return liquid(\"Hello {{shop.name}}\");\n}", - "title": "app/routes/**\\/.ts" - } - ] - }, - { - "title": "Rendering liquid content without a layout", - "description": "Set the `layout` option to `false` to render the Liquid content without a theme.", - "tabs": [ - { - "code": "import {authenticate} from \"~/shopify.server\"\n\nexport async function loader({ request }) {\n const {liquid} = await authenticate.public.appProxy(request);\n\n return liquid(\n \"Hello {{shop.name}}\",\n { layout: false }\n );\n}", - "title": "app/routes/**\\/.ts" - } - ] - } - ] - } - ], - "value": "export interface AppProxyContextWithSession<\n Resources extends ShopifyRestResources = ShopifyRestResources,\n> extends Context {\n /**\n * The session for the shop that made the request.\n *\n * This comes from the session storage which `shopifyApp` uses to store sessions in your database of choice.\n *\n * Use this to get shop or user-specific data.\n *\n * @example\n * Using the session object.\n * Get the session for the shop that initiated the request to the app proxy.\n * ```ts\n * // app/routes/**\\/.ts\n * import { json } from \"@remix-run/node\";\n * import { authenticate } from \"../shopify.server\";\n * import { getMyAppModelData } from \"~/db/model.server\";\n *\n * export const loader = async ({ request }) => {\n * // Get the session for the shop that initiated the request to the app proxy.\n * const { session } = await authenticate.public.appProxy(request);\n *\n * // Use the session data to make to queries to your database or additional requests.\n * return json(await getMyAppModelData({shop: session.shop));\n * };\n * ```\n */\n session: Session;\n\n /**\n * Methods for interacting with the GraphQL / REST Admin APIs for the store that made the request.\n *\n * @example\n * Interacting with the Admin API.\n * Use the `admin` object to interact with the REST or GraphQL APIs.\n * ```ts\n * // app/routes/**\\/.ts\n * import { json } from \"@remix-run/node\";\n * import { authenticate } from \"../shopify.server\";\n *\n * export async function action({ request }: ActionFunctionArgs) {\n * const { admin } = await authenticate.public.appProxy(request);\n *\n * const response = await admin.graphql(\n * `#graphql\n * mutation populateProduct($input: ProductInput!) {\n * productCreate(input: $input) {\n * product {\n * id\n * }\n * }\n * }`,\n * { variables: { input: { title: \"Product Name\" } } }\n * );\n *\n * const productData = await response.json();\n * return json({ data: productData.data });\n * }\n * ```\n */\n admin: AdminApiContext;\n\n /**\n * Method for interacting with the Shopify Storefront Graphql API for the store that made the request.\n *\n * @example\n * Interacting with the Storefront API.\n * Use the `storefront` object to interact with the GraphQL API.\n * ```ts\n * // app/routes/**\\/.ts\n * import { json } from \"@remix-run/node\";\n * import { authenticate } from \"../shopify.server\";\n *\n * export async function action({ request }: ActionFunctionArgs) {\n * const { storefront } = await authenticate.public.appProxy(request);\n *\n * const response = await storefront.graphql(`{blogs(first: 10) { edges { node { id } } } }`);\n *\n * return json(await response.json());\n * }\n * ```\n */\n storefront: StorefrontContext;\n}" - }, - "StorefrontContext": { - "filePath": "src/server/clients/storefront/types.ts", - "name": "StorefrontContext", - "description": "", - "members": [ - { - "filePath": "src/server/clients/storefront/types.ts", - "syntaxKind": "PropertySignature", - "name": "graphql", - "value": "GraphQLClient", - "description": "Method for interacting with the Shopify Storefront GraphQL API\n\nIf 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).\n\n\n\n\n", - "examples": [ - { - "title": "Querying the GraphQL API", - "description": "Use `storefront.graphql` to make query / mutation requests.", - "tabs": [ - { - "code": "import { json } from \"@remix-run/node\";\nimport { authenticate } from \"../shopify.server\";\n\nexport async function action({ request }: ActionFunctionArgs) {\n const { storefront } = await authenticate.public.appProxy(request);\n\n const response = await storefront.graphql(`{blogs(first: 10) { edges { node { id } } } }`);\n\n return json(await response.json());\n}", - "title": "app/routes/**\\/.ts" - } - ] - } - ] - } - ], - "value": "export interface StorefrontContext {\n /**\n * Method for interacting with the Shopify Storefront GraphQL API\n *\n * 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).\n *\n * {@link https://shopify.dev/docs/api/storefront}\n *\n * @example\n * Querying the GraphQL API.\n * Use `storefront.graphql` to make query / mutation requests.\n * ```ts\n * // app/routes/**\\/.ts\n * import { json } from \"@remix-run/node\";\n * import { authenticate } from \"../shopify.server\";\n *\n * export async function action({ request }: ActionFunctionArgs) {\n * const { storefront } = await authenticate.public.appProxy(request);\n *\n * const response = await storefront.graphql(`{blogs(first: 10) { edges { node { id } } } }`);\n *\n * return json(await response.json());\n * }\n * ```\n */\n graphql: GraphQLClient;\n}" - }, - "StorefrontOperations": { - "filePath": "../../node_modules/@shopify/shopify-api/node_modules/@shopify/storefront-api-client/dist/storefront-api-client.d.ts", - "syntaxKind": "TypeAliasDeclaration", - "name": "StorefrontOperations", - "value": "StorefrontQueries & StorefrontMutations", - "description": "" - }, - "StorefrontQueries": { - "filePath": "../../node_modules/@shopify/shopify-api/node_modules/@shopify/storefront-api-client/dist/storefront-api-client.d.ts", - "name": "StorefrontQueries", - "description": "", - "members": [ - { - "filePath": "../../node_modules/@shopify/shopify-api/node_modules/@shopify/storefront-api-client/dist/storefront-api-client.d.ts", - "name": "[key: string]", - "value": "{\n variables: any;\n return: any;\n }" - }, - { - "filePath": "../../node_modules/@shopify/shopify-api/node_modules/@shopify/storefront-api-client/dist/storefront-api-client.d.ts", - "name": "[key: number | symbol]", - "value": "never" - } - ], - "value": "interface StorefrontQueries {\n [key: string]: {\n variables: any;\n return: any;\n };\n [key: number | symbol]: never;\n}" - }, - "StorefrontMutations": { - "filePath": "../../node_modules/@shopify/shopify-api/node_modules/@shopify/storefront-api-client/dist/storefront-api-client.d.ts", - "name": "StorefrontMutations", - "description": "", - "members": [ - { - "filePath": "../../node_modules/@shopify/shopify-api/node_modules/@shopify/storefront-api-client/dist/storefront-api-client.d.ts", - "name": "[key: string]", - "value": "{\n variables: any;\n return: any;\n }" - }, - { - "filePath": "../../node_modules/@shopify/shopify-api/node_modules/@shopify/storefront-api-client/dist/storefront-api-client.d.ts", - "name": "[key: number | symbol]", - "value": "never" - } - ], - "value": "interface StorefrontMutations {\n [key: string]: {\n variables: any;\n return: any;\n };\n [key: number | symbol]: never;\n}" - }, - "AuthenticatePublicLegacy": { - "filePath": "src/server/authenticate/public/types.ts", - "syntaxKind": "TypeAliasDeclaration", - "name": "AuthenticatePublicLegacy", - "value": "AuthenticateCheckout & AuthenticatePublicObject", - "description": "Methods for authenticating Requests from Shopify's public surfaces\n\nTo maintain backwards compatability this is a function and an object.\n\nDo not use `authenticate.public()`. Use `authenticate.public.checkout()` instead. `authenticate.public()` will be removed in v2.\n\nMethods are:\n\n- `authenticate.public.checkout()` for authenticating requests from checkout extensions - `authenticate.public.appProxy()` for authenticating requests from app proxies" - }, - "AuthenticateWebhook": { - "filePath": "src/server/authenticate/webhooks/types.ts", - "name": "AuthenticateWebhook", - "description": "", - "params": [ - { - "name": "request", - "description": "", - "value": "Request", - "filePath": "src/server/authenticate/webhooks/types.ts" - } - ], - "returns": { - "filePath": "src/server/authenticate/webhooks/types.ts", - "description": "", - "name": "Promise>", - "value": "Promise>" - }, - "value": "export type AuthenticateWebhook<\n Future extends FutureFlagOptions,\n Resources extends ShopifyRestResources,\n Topics = string | number | symbol,\n> = (request: Request) => Promise>;" - }, - "WebhookContext": { - "filePath": "src/server/authenticate/webhooks/types.ts", - "syntaxKind": "TypeAliasDeclaration", - "name": "WebhookContext", - "value": "WebhookContextWithoutSession | WebhookContextWithSession", - "description": "" - }, - "WebhookContextWithoutSession": { - "filePath": "src/server/authenticate/webhooks/types.ts", - "name": "WebhookContextWithoutSession", - "description": "", - "members": [ - { - "filePath": "src/server/authenticate/webhooks/types.ts", - "syntaxKind": "PropertySignature", - "name": "session", - "value": "undefined", - "description": "" - }, - { - "filePath": "src/server/authenticate/webhooks/types.ts", - "syntaxKind": "PropertySignature", - "name": "admin", - "value": "undefined", - "description": "" - }, - { - "filePath": "src/server/authenticate/webhooks/types.ts", - "syntaxKind": "PropertySignature", - "name": "apiVersion", - "value": "string", - "description": "The API version used for the webhook.", - "examples": [ - { - "title": "Webhook API version", - "description": "Get the API version used for webhook request.", - "tabs": [ - { - "code": "import { ActionFunction } from \"@remix-run/node\";\nimport { authenticate } from \"../shopify.server\";\n\nexport const action: ActionFunction = async ({ request }) => {\n const { apiVersion } = await authenticate.webhook(request);\n return new Response();\n};", - "title": "/app/routes/webhooks.tsx" - } - ] - } - ] - }, - { - "filePath": "src/server/authenticate/webhooks/types.ts", - "syntaxKind": "PropertySignature", - "name": "shop", - "value": "string", - "description": "The shop where the webhook was triggered.", - "examples": [ - { - "title": "Webhook shop", - "description": "Get the shop that triggered a webhook.", - "tabs": [ - { - "code": "import { ActionFunction } from \"@remix-run/node\";\nimport { authenticate } from \"../shopify.server\";\n\nexport const action: ActionFunction = async ({ request }) => {\n const { shop } = await authenticate.webhook(request);\n return new Response();\n};", - "title": "/app/routes/webhooks.tsx" - } - ] - } - ] - }, - { - "filePath": "src/server/authenticate/webhooks/types.ts", - "syntaxKind": "PropertySignature", - "name": "topic", - "value": "Topics", - "description": "The topic of the webhook.", - "examples": [ - { - "title": "Webhook topic", - "description": "Get the event topic for the webhook.", - "tabs": [ - { - "code": "import { ActionFunction } from \"@remix-run/node\";\nimport { authenticate } from \"../shopify.server\";\n\nexport const action: ActionFunction = async ({ request }) => {\n const { topic } = await authenticate.webhook(request);\n\n switch (topic) {\n case \"APP_UNINSTALLED\":\n // Do something when the app is uninstalled.\n break;\n }\n\n return new Response();\n};", - "title": "/app/routes/webhooks.tsx" - } - ] - } - ] - }, - { - "filePath": "src/server/authenticate/webhooks/types.ts", - "syntaxKind": "PropertySignature", - "name": "webhookId", - "value": "string", - "description": "A unique ID for the webhook. Useful to keep track of which events your app has already processed.", - "examples": [ - { - "title": "Webhook ID", - "description": "Get the webhook ID.", - "tabs": [ - { - "code": "import { ActionFunction } from \"@remix-run/node\";\nimport { authenticate } from \"../shopify.server\";\n\nexport const action: ActionFunction = async ({ request }) => {\n const { webhookId } = await authenticate.webhook(request);\n return new Response();\n};", - "title": "/app/routes/webhooks.tsx" - } - ] - } - ] - }, - { - "filePath": "src/server/authenticate/webhooks/types.ts", - "syntaxKind": "PropertySignature", - "name": "payload", - "value": "JSONValue", - "description": "The payload from the webhook request.", - "examples": [ - { - "title": "Webhook payload", - "description": "Get the request's POST payload.", - "tabs": [ - { - "code": "import { ActionFunction } from \"@remix-run/node\";\nimport { authenticate } from \"../shopify.server\";\n\nexport const action: ActionFunction = async ({ request }) => {\n const { payload } = await authenticate.webhook(request);\n return new Response();\n};", - "title": "/app/routes/webhooks.tsx" - } - ] - } - ] - }, - { - "filePath": "src/server/authenticate/webhooks/types.ts", - "syntaxKind": "PropertySignature", - "name": "subTopic", - "value": "string", - "description": "The sub-topic of the webhook. This is only available for certain webhooks.", - "isOptional": true, - "examples": [ - { - "title": "Webhook sub-topic", - "description": "Get the webhook sub-topic.", - "tabs": [ - { - "code": "import { ActionFunction } from \"@remix-run/node\";\nimport { authenticate } from \"../shopify.server\";\n\nexport const action: ActionFunction = async ({ request }) => {\n const { subTopic } = await authenticate.webhook(request);\n return new Response();\n};", - "title": "/app/routes/webhooks.tsx" - } - ] - } - ] - } - ], - "value": "export interface WebhookContextWithoutSession\n extends Context {\n session: undefined;\n admin: undefined;\n}" + "value": "export interface WebhookContextWithoutSession\n extends Context {\n session: undefined;\n admin: undefined;\n}" }, "JSONValue": { "filePath": "src/server/types.ts", @@ -13730,7 +13680,7 @@ "description": "Get the API version used for webhook request.", "tabs": [ { - "code": "import { ActionFunction } from \"@remix-run/node\";\nimport { authenticate } from \"../shopify.server\";\n\nexport const action: ActionFunction = async ({ request }) => {\n const { apiVersion } = await authenticate.webhook(request);\n return new Response();\n};", + "code": "import { ActionFunctionArgs } from \"@remix-run/node\";\nimport { authenticate } from \"../shopify.server\";\n\nexport const action = async ({ request }: ActionFunctionArgs) => {\n const { apiVersion } = await authenticate.webhook(request);\n return new Response();\n};", "title": "/app/routes/webhooks.tsx" } ] @@ -13749,7 +13699,7 @@ "description": "Get the shop that triggered a webhook.", "tabs": [ { - "code": "import { ActionFunction } from \"@remix-run/node\";\nimport { authenticate } from \"../shopify.server\";\n\nexport const action: ActionFunction = async ({ request }) => {\n const { shop } = await authenticate.webhook(request);\n return new Response();\n};", + "code": "import { ActionFunctionArgs } from \"@remix-run/node\";\nimport { authenticate } from \"../shopify.server\";\n\nexport const action = async ({ request }: ActionFunctionArgs) => {\n const { shop } = await authenticate.webhook(request);\n return new Response();\n};", "title": "/app/routes/webhooks.tsx" } ] @@ -13768,7 +13718,7 @@ "description": "Get the event topic for the webhook.", "tabs": [ { - "code": "import { ActionFunction } from \"@remix-run/node\";\nimport { authenticate } from \"../shopify.server\";\n\nexport const action: ActionFunction = async ({ request }) => {\n const { topic } = await authenticate.webhook(request);\n\n switch (topic) {\n case \"APP_UNINSTALLED\":\n // Do something when the app is uninstalled.\n break;\n }\n\n return new Response();\n};", + "code": "import { ActionFunctionArgs } from \"@remix-run/node\";\nimport { authenticate } from \"../shopify.server\";\n\nexport const action = async ({ request }: ActionFunctionArgs) => {\n const { topic } = await authenticate.webhook(request);\n\n switch (topic) {\n case \"APP_UNINSTALLED\":\n // Do something when the app is uninstalled.\n break;\n }\n\n return new Response();\n};", "title": "/app/routes/webhooks.tsx" } ] @@ -13787,7 +13737,7 @@ "description": "Get the webhook ID.", "tabs": [ { - "code": "import { ActionFunction } from \"@remix-run/node\";\nimport { authenticate } from \"../shopify.server\";\n\nexport const action: ActionFunction = async ({ request }) => {\n const { webhookId } = await authenticate.webhook(request);\n return new Response();\n};", + "code": "import { ActionFunctionArgs } from \"@remix-run/node\";\nimport { authenticate } from \"../shopify.server\";\n\nexport const action = async ({ request }: ActionFunctionArgs) => {\n const { webhookId } = await authenticate.webhook(request);\n return new Response();\n};", "title": "/app/routes/webhooks.tsx" } ] @@ -13806,7 +13756,7 @@ "description": "Get the request's POST payload.", "tabs": [ { - "code": "import { ActionFunction } from \"@remix-run/node\";\nimport { authenticate } from \"../shopify.server\";\n\nexport const action: ActionFunction = async ({ request }) => {\n const { payload } = await authenticate.webhook(request);\n return new Response();\n};", + "code": "import { ActionFunctionArgs } from \"@remix-run/node\";\nimport { authenticate } from \"../shopify.server\";\n\nexport const action = async ({ request }: ActionFunctionArgs) => {\n const { payload } = await authenticate.webhook(request);\n return new Response();\n};", "title": "/app/routes/webhooks.tsx" } ] @@ -13826,7 +13776,7 @@ "description": "Get the webhook sub-topic.", "tabs": [ { - "code": "import { ActionFunction } from \"@remix-run/node\";\nimport { authenticate } from \"../shopify.server\";\n\nexport const action: ActionFunction = async ({ request }) => {\n const { subTopic } = await authenticate.webhook(request);\n return new Response();\n};", + "code": "import { ActionFunctionArgs } from \"@remix-run/node\";\nimport { authenticate } from \"../shopify.server\";\n\nexport const action = async ({ request }: ActionFunctionArgs) => {\n const { subTopic } = await authenticate.webhook(request);\n return new Response();\n};", "title": "/app/routes/webhooks.tsx" } ] @@ -14493,368 +14443,814 @@ }, { "filePath": "../../node_modules/@shopify/polaris/build/ts/src/types.d.ts", - "name": "Key6", - "value": 54 + "name": "Key6", + "value": 54 + }, + { + "filePath": "../../node_modules/@shopify/polaris/build/ts/src/types.d.ts", + "name": "Key7", + "value": 55 + }, + { + "filePath": "../../node_modules/@shopify/polaris/build/ts/src/types.d.ts", + "name": "Key8", + "value": 56 + }, + { + "filePath": "../../node_modules/@shopify/polaris/build/ts/src/types.d.ts", + "name": "Key9", + "value": 57 + }, + { + "filePath": "../../node_modules/@shopify/polaris/build/ts/src/types.d.ts", + "name": "KeyA", + "value": 65 + }, + { + "filePath": "../../node_modules/@shopify/polaris/build/ts/src/types.d.ts", + "name": "KeyB", + "value": 66 + }, + { + "filePath": "../../node_modules/@shopify/polaris/build/ts/src/types.d.ts", + "name": "KeyC", + "value": 67 + }, + { + "filePath": "../../node_modules/@shopify/polaris/build/ts/src/types.d.ts", + "name": "KeyD", + "value": 68 + }, + { + "filePath": "../../node_modules/@shopify/polaris/build/ts/src/types.d.ts", + "name": "KeyE", + "value": 69 + }, + { + "filePath": "../../node_modules/@shopify/polaris/build/ts/src/types.d.ts", + "name": "KeyF", + "value": 70 + }, + { + "filePath": "../../node_modules/@shopify/polaris/build/ts/src/types.d.ts", + "name": "KeyG", + "value": 71 + }, + { + "filePath": "../../node_modules/@shopify/polaris/build/ts/src/types.d.ts", + "name": "KeyH", + "value": 72 + }, + { + "filePath": "../../node_modules/@shopify/polaris/build/ts/src/types.d.ts", + "name": "KeyI", + "value": 73 + }, + { + "filePath": "../../node_modules/@shopify/polaris/build/ts/src/types.d.ts", + "name": "KeyJ", + "value": 74 + }, + { + "filePath": "../../node_modules/@shopify/polaris/build/ts/src/types.d.ts", + "name": "KeyK", + "value": 75 + }, + { + "filePath": "../../node_modules/@shopify/polaris/build/ts/src/types.d.ts", + "name": "KeyL", + "value": 76 + }, + { + "filePath": "../../node_modules/@shopify/polaris/build/ts/src/types.d.ts", + "name": "KeyM", + "value": 77 + }, + { + "filePath": "../../node_modules/@shopify/polaris/build/ts/src/types.d.ts", + "name": "KeyN", + "value": 78 + }, + { + "filePath": "../../node_modules/@shopify/polaris/build/ts/src/types.d.ts", + "name": "KeyO", + "value": 79 + }, + { + "filePath": "../../node_modules/@shopify/polaris/build/ts/src/types.d.ts", + "name": "KeyP", + "value": 80 + }, + { + "filePath": "../../node_modules/@shopify/polaris/build/ts/src/types.d.ts", + "name": "KeyQ", + "value": 81 + }, + { + "filePath": "../../node_modules/@shopify/polaris/build/ts/src/types.d.ts", + "name": "KeyR", + "value": 82 + }, + { + "filePath": "../../node_modules/@shopify/polaris/build/ts/src/types.d.ts", + "name": "KeyS", + "value": 83 + }, + { + "filePath": "../../node_modules/@shopify/polaris/build/ts/src/types.d.ts", + "name": "KeyT", + "value": 84 + }, + { + "filePath": "../../node_modules/@shopify/polaris/build/ts/src/types.d.ts", + "name": "KeyU", + "value": 85 + }, + { + "filePath": "../../node_modules/@shopify/polaris/build/ts/src/types.d.ts", + "name": "KeyV", + "value": 86 + }, + { + "filePath": "../../node_modules/@shopify/polaris/build/ts/src/types.d.ts", + "name": "KeyW", + "value": 87 + }, + { + "filePath": "../../node_modules/@shopify/polaris/build/ts/src/types.d.ts", + "name": "KeyX", + "value": 88 + }, + { + "filePath": "../../node_modules/@shopify/polaris/build/ts/src/types.d.ts", + "name": "KeyY", + "value": 89 + }, + { + "filePath": "../../node_modules/@shopify/polaris/build/ts/src/types.d.ts", + "name": "KeyZ", + "value": 90 + }, + { + "filePath": "../../node_modules/@shopify/polaris/build/ts/src/types.d.ts", + "name": "LeftMeta", + "value": 91 + }, + { + "filePath": "../../node_modules/@shopify/polaris/build/ts/src/types.d.ts", + "name": "RightMeta", + "value": 92 + }, + { + "filePath": "../../node_modules/@shopify/polaris/build/ts/src/types.d.ts", + "name": "Select", + "value": 93 + }, + { + "filePath": "../../node_modules/@shopify/polaris/build/ts/src/types.d.ts", + "name": "Numpad0", + "value": 96 + }, + { + "filePath": "../../node_modules/@shopify/polaris/build/ts/src/types.d.ts", + "name": "Numpad1", + "value": 97 + }, + { + "filePath": "../../node_modules/@shopify/polaris/build/ts/src/types.d.ts", + "name": "Numpad2", + "value": 98 + }, + { + "filePath": "../../node_modules/@shopify/polaris/build/ts/src/types.d.ts", + "name": "Numpad3", + "value": 99 + }, + { + "filePath": "../../node_modules/@shopify/polaris/build/ts/src/types.d.ts", + "name": "Numpad4", + "value": 100 + }, + { + "filePath": "../../node_modules/@shopify/polaris/build/ts/src/types.d.ts", + "name": "Numpad5", + "value": 101 + }, + { + "filePath": "../../node_modules/@shopify/polaris/build/ts/src/types.d.ts", + "name": "Numpad6", + "value": 102 + }, + { + "filePath": "../../node_modules/@shopify/polaris/build/ts/src/types.d.ts", + "name": "Numpad7", + "value": 103 }, { "filePath": "../../node_modules/@shopify/polaris/build/ts/src/types.d.ts", - "name": "Key7", - "value": 55 + "name": "Numpad8", + "value": 104 }, { "filePath": "../../node_modules/@shopify/polaris/build/ts/src/types.d.ts", - "name": "Key8", - "value": 56 + "name": "Numpad9", + "value": 105 }, { "filePath": "../../node_modules/@shopify/polaris/build/ts/src/types.d.ts", - "name": "Key9", - "value": 57 + "name": "Multiply", + "value": 106 }, { "filePath": "../../node_modules/@shopify/polaris/build/ts/src/types.d.ts", - "name": "KeyA", - "value": 65 + "name": "Add", + "value": 107 }, { "filePath": "../../node_modules/@shopify/polaris/build/ts/src/types.d.ts", - "name": "KeyB", - "value": 66 + "name": "Subtract", + "value": 109 }, { "filePath": "../../node_modules/@shopify/polaris/build/ts/src/types.d.ts", - "name": "KeyC", - "value": 67 + "name": "Decimal", + "value": 110 }, { "filePath": "../../node_modules/@shopify/polaris/build/ts/src/types.d.ts", - "name": "KeyD", - "value": 68 + "name": "Divide", + "value": 111 }, { "filePath": "../../node_modules/@shopify/polaris/build/ts/src/types.d.ts", - "name": "KeyE", - "value": 69 + "name": "F1", + "value": 112 }, { "filePath": "../../node_modules/@shopify/polaris/build/ts/src/types.d.ts", - "name": "KeyF", - "value": 70 + "name": "F2", + "value": 113 }, { "filePath": "../../node_modules/@shopify/polaris/build/ts/src/types.d.ts", - "name": "KeyG", - "value": 71 + "name": "F3", + "value": 114 }, { "filePath": "../../node_modules/@shopify/polaris/build/ts/src/types.d.ts", - "name": "KeyH", - "value": 72 + "name": "F4", + "value": 115 }, { "filePath": "../../node_modules/@shopify/polaris/build/ts/src/types.d.ts", - "name": "KeyI", - "value": 73 + "name": "F5", + "value": 116 }, { "filePath": "../../node_modules/@shopify/polaris/build/ts/src/types.d.ts", - "name": "KeyJ", - "value": 74 + "name": "F6", + "value": 117 }, { "filePath": "../../node_modules/@shopify/polaris/build/ts/src/types.d.ts", - "name": "KeyK", - "value": 75 + "name": "F7", + "value": 118 }, { "filePath": "../../node_modules/@shopify/polaris/build/ts/src/types.d.ts", - "name": "KeyL", - "value": 76 + "name": "F8", + "value": 119 }, { "filePath": "../../node_modules/@shopify/polaris/build/ts/src/types.d.ts", - "name": "KeyM", - "value": 77 + "name": "F9", + "value": 120 }, { "filePath": "../../node_modules/@shopify/polaris/build/ts/src/types.d.ts", - "name": "KeyN", - "value": 78 + "name": "F10", + "value": 121 }, { "filePath": "../../node_modules/@shopify/polaris/build/ts/src/types.d.ts", - "name": "KeyO", - "value": 79 + "name": "F11", + "value": 122 }, { "filePath": "../../node_modules/@shopify/polaris/build/ts/src/types.d.ts", - "name": "KeyP", - "value": 80 + "name": "F12", + "value": 123 }, { "filePath": "../../node_modules/@shopify/polaris/build/ts/src/types.d.ts", - "name": "KeyQ", - "value": 81 + "name": "NumLock", + "value": 144 }, { "filePath": "../../node_modules/@shopify/polaris/build/ts/src/types.d.ts", - "name": "KeyR", - "value": 82 + "name": "ScrollLock", + "value": 145 }, { "filePath": "../../node_modules/@shopify/polaris/build/ts/src/types.d.ts", - "name": "KeyS", - "value": 83 + "name": "Semicolon", + "value": 186 }, { "filePath": "../../node_modules/@shopify/polaris/build/ts/src/types.d.ts", - "name": "KeyT", - "value": 84 + "name": "Equals", + "value": 187 }, { "filePath": "../../node_modules/@shopify/polaris/build/ts/src/types.d.ts", - "name": "KeyU", - "value": 85 + "name": "Comma", + "value": 188 }, { "filePath": "../../node_modules/@shopify/polaris/build/ts/src/types.d.ts", - "name": "KeyV", - "value": 86 + "name": "Dash", + "value": 189 }, { "filePath": "../../node_modules/@shopify/polaris/build/ts/src/types.d.ts", - "name": "KeyW", - "value": 87 + "name": "Period", + "value": 190 }, { "filePath": "../../node_modules/@shopify/polaris/build/ts/src/types.d.ts", - "name": "KeyX", - "value": 88 + "name": "ForwardSlash", + "value": 191 }, { "filePath": "../../node_modules/@shopify/polaris/build/ts/src/types.d.ts", - "name": "KeyY", - "value": 89 + "name": "GraveAccent", + "value": 192 }, { "filePath": "../../node_modules/@shopify/polaris/build/ts/src/types.d.ts", - "name": "KeyZ", - "value": 90 + "name": "OpenBracket", + "value": 219 }, { "filePath": "../../node_modules/@shopify/polaris/build/ts/src/types.d.ts", - "name": "LeftMeta", - "value": 91 + "name": "BackSlash", + "value": 220 }, { "filePath": "../../node_modules/@shopify/polaris/build/ts/src/types.d.ts", - "name": "RightMeta", - "value": 92 + "name": "CloseBracket", + "value": 221 }, { "filePath": "../../node_modules/@shopify/polaris/build/ts/src/types.d.ts", - "name": "Select", - "value": 93 + "name": "SingleQuote", + "value": 222 + } + ] + }, + "BillingConfig": { + "filePath": "../../node_modules/@shopify/shopify-api/lib/billing/types.d.ts", + "name": "BillingConfig", + "description": "Billing configuration options, indexed by an app-specific plan name.", + "members": [ + { + "filePath": "../../node_modules/@shopify/shopify-api/lib/billing/types.d.ts", + "name": "[plan: string]", + "value": "BillingConfigItem" + } + ], + "value": "export interface BillingConfig {\n /**\n * An individual billing plan.\n */\n [plan: string]: BillingConfigItem;\n}" + }, + "BillingConfigItem": { + "filePath": "../../node_modules/@shopify/shopify-api/lib/billing/types.d.ts", + "syntaxKind": "TypeAliasDeclaration", + "name": "BillingConfigItem", + "value": "FeatureEnabled extends true ? BillingConfigOneTimePlan | BillingConfigSubscriptionLineItemPlan : BillingConfigLegacyItem", + "description": "" + }, + "BillingConfigOneTimePlan": { + "filePath": "../../node_modules/@shopify/shopify-api/lib/billing/types.d.ts", + "name": "BillingConfigOneTimePlan", + "description": "", + "members": [ + { + "filePath": "../../node_modules/@shopify/shopify-api/lib/billing/types.d.ts", + "syntaxKind": "PropertySignature", + "name": "interval", + "value": "BillingInterval.OneTime", + "description": "Interval for this plan.\n\nMust be set to `OneTime`." }, { - "filePath": "../../node_modules/@shopify/polaris/build/ts/src/types.d.ts", - "name": "Numpad0", - "value": 96 + "filePath": "../../node_modules/@shopify/shopify-api/lib/billing/types.d.ts", + "syntaxKind": "PropertySignature", + "name": "amount", + "value": "number", + "description": "Amount to charge for this plan." }, { - "filePath": "../../node_modules/@shopify/polaris/build/ts/src/types.d.ts", - "name": "Numpad1", - "value": 97 + "filePath": "../../node_modules/@shopify/shopify-api/lib/billing/types.d.ts", + "syntaxKind": "PropertySignature", + "name": "currencyCode", + "value": "string", + "description": "Currency code for this plan." + } + ], + "value": "export interface BillingConfigOneTimePlan extends BillingConfigPlan {\n /**\n * Interval for this plan.\n *\n * Must be set to `OneTime`.\n */\n interval: BillingInterval.OneTime;\n}" + }, + "BillingInterval": { + "filePath": "../../node_modules/@shopify/shopify-api/lib/types.d.ts", + "syntaxKind": "EnumDeclaration", + "name": "BillingInterval", + "value": "export declare enum BillingInterval {\n OneTime = \"ONE_TIME\",\n Every30Days = \"EVERY_30_DAYS\",\n Annual = \"ANNUAL\",\n Usage = \"USAGE\"\n}", + "members": [ + { + "filePath": "../../node_modules/@shopify/shopify-api/lib/types.d.ts", + "name": "OneTime", + "value": "ONE_TIME" }, { - "filePath": "../../node_modules/@shopify/polaris/build/ts/src/types.d.ts", - "name": "Numpad2", - "value": 98 + "filePath": "../../node_modules/@shopify/shopify-api/lib/types.d.ts", + "name": "Every30Days", + "value": "EVERY_30_DAYS" }, { - "filePath": "../../node_modules/@shopify/polaris/build/ts/src/types.d.ts", - "name": "Numpad3", - "value": 99 + "filePath": "../../node_modules/@shopify/shopify-api/lib/types.d.ts", + "name": "Annual", + "value": "ANNUAL" }, { - "filePath": "../../node_modules/@shopify/polaris/build/ts/src/types.d.ts", - "name": "Numpad4", - "value": 100 + "filePath": "../../node_modules/@shopify/shopify-api/lib/types.d.ts", + "name": "Usage", + "value": "USAGE" + } + ] + }, + "BillingConfigSubscriptionLineItemPlan": { + "filePath": "../../node_modules/@shopify/shopify-api/lib/billing/types.d.ts", + "name": "BillingConfigSubscriptionLineItemPlan", + "description": "", + "members": [ + { + "filePath": "../../node_modules/@shopify/shopify-api/lib/billing/types.d.ts", + "syntaxKind": "PropertySignature", + "name": "replacementBehavior", + "value": "BillingReplacementBehavior", + "description": "The replacement behavior to use for this plan.", + "isOptional": true }, { - "filePath": "../../node_modules/@shopify/polaris/build/ts/src/types.d.ts", - "name": "Numpad5", - "value": 101 + "filePath": "../../node_modules/@shopify/shopify-api/lib/billing/types.d.ts", + "syntaxKind": "PropertySignature", + "name": "trialDays", + "value": "number", + "description": "How many trial days to give before charging for this plan.", + "isOptional": true }, { - "filePath": "../../node_modules/@shopify/polaris/build/ts/src/types.d.ts", - "name": "Numpad6", - "value": 102 + "filePath": "../../node_modules/@shopify/shopify-api/lib/billing/types.d.ts", + "syntaxKind": "PropertySignature", + "name": "lineItems", + "value": "(BillingConfigRecurringLineItem | BillingConfigUsageLineItem)[]", + "description": "The line items for this plan." + } + ], + "value": "export interface BillingConfigSubscriptionLineItemPlan {\n /**\n * The replacement behavior to use for this plan.\n */\n replacementBehavior?: BillingReplacementBehavior;\n /**\n * How many trial days to give before charging for this plan.\n */\n trialDays?: number;\n /**\n * The line items for this plan.\n */\n lineItems: (BillingConfigRecurringLineItem | BillingConfigUsageLineItem)[];\n}" + }, + "BillingReplacementBehavior": { + "filePath": "../../node_modules/@shopify/shopify-api/lib/types.d.ts", + "syntaxKind": "EnumDeclaration", + "name": "BillingReplacementBehavior", + "value": "export declare enum BillingReplacementBehavior {\n ApplyImmediately = \"APPLY_IMMEDIATELY\",\n ApplyOnNextBillingCycle = \"APPLY_ON_NEXT_BILLING_CYCLE\",\n Standard = \"STANDARD\"\n}", + "members": [ + { + "filePath": "../../node_modules/@shopify/shopify-api/lib/types.d.ts", + "name": "ApplyImmediately", + "value": "APPLY_IMMEDIATELY" }, { - "filePath": "../../node_modules/@shopify/polaris/build/ts/src/types.d.ts", - "name": "Numpad7", - "value": 103 + "filePath": "../../node_modules/@shopify/shopify-api/lib/types.d.ts", + "name": "ApplyOnNextBillingCycle", + "value": "APPLY_ON_NEXT_BILLING_CYCLE" }, { - "filePath": "../../node_modules/@shopify/polaris/build/ts/src/types.d.ts", - "name": "Numpad8", - "value": 104 - }, + "filePath": "../../node_modules/@shopify/shopify-api/lib/types.d.ts", + "name": "Standard", + "value": "STANDARD" + } + ] + }, + "BillingConfigRecurringLineItem": { + "filePath": "../../node_modules/@shopify/shopify-api/lib/billing/types.d.ts", + "name": "BillingConfigRecurringLineItem", + "description": "", + "members": [ { - "filePath": "../../node_modules/@shopify/polaris/build/ts/src/types.d.ts", - "name": "Numpad9", - "value": 105 + "filePath": "../../node_modules/@shopify/shopify-api/lib/billing/types.d.ts", + "syntaxKind": "PropertySignature", + "name": "interval", + "value": "BillingInterval.Every30Days | BillingInterval.Annual", + "description": "The recurring interval for this line item.\n\nMust be either `Every30Days` or `Annual`." }, { - "filePath": "../../node_modules/@shopify/polaris/build/ts/src/types.d.ts", - "name": "Multiply", - "value": 106 + "filePath": "../../node_modules/@shopify/shopify-api/lib/billing/types.d.ts", + "syntaxKind": "PropertySignature", + "name": "discount", + "value": "BillingConfigSubscriptionPlanDiscount", + "description": "An optional discount to apply for this line item.", + "isOptional": true }, { - "filePath": "../../node_modules/@shopify/polaris/build/ts/src/types.d.ts", - "name": "Add", - "value": 107 + "filePath": "../../node_modules/@shopify/shopify-api/lib/billing/types.d.ts", + "syntaxKind": "PropertySignature", + "name": "amount", + "value": "number", + "description": "The amount to charge for this line item." }, { - "filePath": "../../node_modules/@shopify/polaris/build/ts/src/types.d.ts", - "name": "Subtract", - "value": 109 - }, + "filePath": "../../node_modules/@shopify/shopify-api/lib/billing/types.d.ts", + "syntaxKind": "PropertySignature", + "name": "currencyCode", + "value": "string", + "description": "The currency code for this line item." + } + ], + "value": "export interface BillingConfigRecurringLineItem extends BillingConfigLineItem {\n /**\n * The recurring interval for this line item.\n *\n * Must be either `Every30Days` or `Annual`.\n */\n interval: BillingInterval.Every30Days | BillingInterval.Annual;\n /**\n * An optional discount to apply for this line item.\n */\n discount?: BillingConfigSubscriptionPlanDiscount;\n}" + }, + "BillingConfigSubscriptionPlanDiscount": { + "filePath": "../../node_modules/@shopify/shopify-api/lib/billing/types.d.ts", + "name": "BillingConfigSubscriptionPlanDiscount", + "description": "", + "members": [ { - "filePath": "../../node_modules/@shopify/polaris/build/ts/src/types.d.ts", - "name": "Decimal", - "value": 110 + "filePath": "../../node_modules/@shopify/shopify-api/lib/billing/types.d.ts", + "syntaxKind": "PropertySignature", + "name": "durationLimitInIntervals", + "value": "number", + "description": "The number of intervals to apply the discount for.", + "isOptional": true }, { - "filePath": "../../node_modules/@shopify/polaris/build/ts/src/types.d.ts", - "name": "Divide", - "value": 111 - }, + "filePath": "../../node_modules/@shopify/shopify-api/lib/billing/types.d.ts", + "syntaxKind": "PropertySignature", + "name": "value", + "value": "BillingConfigSubscriptionPlanDiscountAmount | BillingConfigSubscriptionPlanDiscountPercentage", + "description": "The discount to apply." + } + ], + "value": "export interface BillingConfigSubscriptionPlanDiscount {\n /**\n * The number of intervals to apply the discount for.\n */\n durationLimitInIntervals?: number;\n /**\n * The discount to apply.\n */\n value: BillingConfigSubscriptionPlanDiscountAmount | BillingConfigSubscriptionPlanDiscountPercentage;\n}" + }, + "BillingConfigSubscriptionPlanDiscountAmount": { + "filePath": "../../node_modules/@shopify/shopify-api/lib/billing/types.d.ts", + "name": "BillingConfigSubscriptionPlanDiscountAmount", + "description": "", + "members": [ { - "filePath": "../../node_modules/@shopify/polaris/build/ts/src/types.d.ts", - "name": "F1", - "value": 112 + "filePath": "../../node_modules/@shopify/shopify-api/lib/billing/types.d.ts", + "syntaxKind": "PropertySignature", + "name": "amount", + "value": "number", + "description": "The amount to discount.\n\nCannot be set if `percentage` is set." }, { - "filePath": "../../node_modules/@shopify/polaris/build/ts/src/types.d.ts", - "name": "F2", - "value": 113 - }, + "filePath": "../../node_modules/@shopify/shopify-api/lib/billing/types.d.ts", + "syntaxKind": "PropertySignature", + "name": "percentage", + "value": "never", + "description": "The percentage to discount.\n\nCannot be set if `amount` is set.", + "isOptional": true + } + ], + "value": "export interface BillingConfigSubscriptionPlanDiscountAmount {\n /**\n * The amount to discount.\n *\n * Cannot be set if `percentage` is set.\n */\n amount: number;\n /**\n * The percentage to discount.\n *\n * Cannot be set if `amount` is set.\n */\n percentage?: never;\n}" + }, + "BillingConfigSubscriptionPlanDiscountPercentage": { + "filePath": "../../node_modules/@shopify/shopify-api/lib/billing/types.d.ts", + "name": "BillingConfigSubscriptionPlanDiscountPercentage", + "description": "", + "members": [ { - "filePath": "../../node_modules/@shopify/polaris/build/ts/src/types.d.ts", - "name": "F3", - "value": 114 + "filePath": "../../node_modules/@shopify/shopify-api/lib/billing/types.d.ts", + "syntaxKind": "PropertySignature", + "name": "amount", + "value": "never", + "description": "The amount to discount.\n\nCannot be set if `percentage` is set.", + "isOptional": true }, { - "filePath": "../../node_modules/@shopify/polaris/build/ts/src/types.d.ts", - "name": "F4", - "value": 115 - }, + "filePath": "../../node_modules/@shopify/shopify-api/lib/billing/types.d.ts", + "syntaxKind": "PropertySignature", + "name": "percentage", + "value": "number", + "description": "The percentage to discount.\n\nCannot be set if `amount` is set." + } + ], + "value": "export interface BillingConfigSubscriptionPlanDiscountPercentage {\n /**\n * The amount to discount.\n *\n * Cannot be set if `percentage` is set.\n */\n amount?: never;\n /**\n * The percentage to discount.\n *\n * Cannot be set if `amount` is set.\n */\n percentage: number;\n}" + }, + "BillingConfigUsageLineItem": { + "filePath": "../../node_modules/@shopify/shopify-api/lib/billing/types.d.ts", + "name": "BillingConfigUsageLineItem", + "description": "", + "members": [ { - "filePath": "../../node_modules/@shopify/polaris/build/ts/src/types.d.ts", - "name": "F5", - "value": 116 + "filePath": "../../node_modules/@shopify/shopify-api/lib/billing/types.d.ts", + "syntaxKind": "PropertySignature", + "name": "interval", + "value": "BillingInterval.Usage", + "description": "The usage interval for this line item.\n\nMust be set to `Usage`." }, { - "filePath": "../../node_modules/@shopify/polaris/build/ts/src/types.d.ts", - "name": "F6", - "value": 117 + "filePath": "../../node_modules/@shopify/shopify-api/lib/billing/types.d.ts", + "syntaxKind": "PropertySignature", + "name": "terms", + "value": "string", + "description": "Usage terms for this line item." }, { - "filePath": "../../node_modules/@shopify/polaris/build/ts/src/types.d.ts", - "name": "F7", - "value": 118 + "filePath": "../../node_modules/@shopify/shopify-api/lib/billing/types.d.ts", + "syntaxKind": "PropertySignature", + "name": "amount", + "value": "number", + "description": "The amount to charge for this line item." }, { - "filePath": "../../node_modules/@shopify/polaris/build/ts/src/types.d.ts", - "name": "F8", - "value": 119 - }, + "filePath": "../../node_modules/@shopify/shopify-api/lib/billing/types.d.ts", + "syntaxKind": "PropertySignature", + "name": "currencyCode", + "value": "string", + "description": "The currency code for this line item." + } + ], + "value": "export interface BillingConfigUsageLineItem extends BillingConfigLineItem {\n /**\n * The usage interval for this line item.\n *\n * Must be set to `Usage`.\n */\n interval: BillingInterval.Usage;\n /**\n * Usage terms for this line item.\n */\n terms: string;\n}" + }, + "BillingConfigLegacyItem": { + "filePath": "../../node_modules/@shopify/shopify-api/lib/billing/types.d.ts", + "syntaxKind": "TypeAliasDeclaration", + "name": "BillingConfigLegacyItem", + "value": "BillingConfigOneTimePlan | BillingConfigSubscriptionPlan | BillingConfigUsagePlan", + "description": "" + }, + "BillingConfigSubscriptionPlan": { + "filePath": "../../node_modules/@shopify/shopify-api/lib/billing/types.d.ts", + "name": "BillingConfigSubscriptionPlan", + "description": "", + "members": [ { - "filePath": "../../node_modules/@shopify/polaris/build/ts/src/types.d.ts", - "name": "F9", - "value": 120 + "filePath": "../../node_modules/@shopify/shopify-api/lib/billing/types.d.ts", + "syntaxKind": "PropertySignature", + "name": "interval", + "value": "Exclude", + "description": "Recurring interval for this plan.\n\nMust be either `Every30Days` or `Annual`." }, { - "filePath": "../../node_modules/@shopify/polaris/build/ts/src/types.d.ts", - "name": "F10", - "value": 121 + "filePath": "../../node_modules/@shopify/shopify-api/lib/billing/types.d.ts", + "syntaxKind": "PropertySignature", + "name": "trialDays", + "value": "number", + "description": "How many trial days to give before charging for this plan.", + "isOptional": true }, { - "filePath": "../../node_modules/@shopify/polaris/build/ts/src/types.d.ts", - "name": "F11", - "value": 122 + "filePath": "../../node_modules/@shopify/shopify-api/lib/billing/types.d.ts", + "syntaxKind": "PropertySignature", + "name": "replacementBehavior", + "value": "BillingReplacementBehavior", + "description": "The behavior to use when replacing an existing subscription with a new one.", + "isOptional": true }, { - "filePath": "../../node_modules/@shopify/polaris/build/ts/src/types.d.ts", - "name": "F12", - "value": 123 + "filePath": "../../node_modules/@shopify/shopify-api/lib/billing/types.d.ts", + "syntaxKind": "PropertySignature", + "name": "discount", + "value": "BillingConfigSubscriptionPlanDiscount", + "description": "The discount to apply to this plan.", + "isOptional": true }, { - "filePath": "../../node_modules/@shopify/polaris/build/ts/src/types.d.ts", - "name": "NumLock", - "value": 144 + "filePath": "../../node_modules/@shopify/shopify-api/lib/billing/types.d.ts", + "syntaxKind": "PropertySignature", + "name": "amount", + "value": "number", + "description": "Amount to charge for this plan." }, { - "filePath": "../../node_modules/@shopify/polaris/build/ts/src/types.d.ts", - "name": "ScrollLock", - "value": 145 + "filePath": "../../node_modules/@shopify/shopify-api/lib/billing/types.d.ts", + "syntaxKind": "PropertySignature", + "name": "currencyCode", + "value": "string", + "description": "Currency code for this plan." + } + ], + "value": "export interface BillingConfigSubscriptionPlan extends BillingConfigPlan {\n /**\n * Recurring interval for this plan.\n *\n * Must be either `Every30Days` or `Annual`.\n */\n interval: Exclude;\n /**\n * How many trial days to give before charging for this plan.\n */\n trialDays?: number;\n /**\n * The behavior to use when replacing an existing subscription with a new one.\n */\n replacementBehavior?: BillingReplacementBehavior;\n /**\n * The discount to apply to this plan.\n */\n discount?: BillingConfigSubscriptionPlanDiscount;\n}" + }, + "RecurringBillingIntervals": { + "filePath": "../../node_modules/@shopify/shopify-api/lib/types.d.ts", + "syntaxKind": "TypeAliasDeclaration", + "name": "RecurringBillingIntervals", + "value": "RecurringBillingIntervals", + "description": "" + }, + "BillingConfigUsagePlan": { + "filePath": "../../node_modules/@shopify/shopify-api/lib/billing/types.d.ts", + "name": "BillingConfigUsagePlan", + "description": "", + "members": [ + { + "filePath": "../../node_modules/@shopify/shopify-api/lib/billing/types.d.ts", + "syntaxKind": "PropertySignature", + "name": "interval", + "value": "BillingInterval.Usage", + "description": "Interval for this plan.\n\nMust be set to `Usage`." }, { - "filePath": "../../node_modules/@shopify/polaris/build/ts/src/types.d.ts", - "name": "Semicolon", - "value": 186 + "filePath": "../../node_modules/@shopify/shopify-api/lib/billing/types.d.ts", + "syntaxKind": "PropertySignature", + "name": "usageTerms", + "value": "string", + "description": "Usage terms for this plan." }, { - "filePath": "../../node_modules/@shopify/polaris/build/ts/src/types.d.ts", - "name": "Equals", - "value": 187 + "filePath": "../../node_modules/@shopify/shopify-api/lib/billing/types.d.ts", + "syntaxKind": "PropertySignature", + "name": "trialDays", + "value": "number", + "description": "How many trial days to give before charging for this plan.", + "isOptional": true }, { - "filePath": "../../node_modules/@shopify/polaris/build/ts/src/types.d.ts", - "name": "Comma", - "value": 188 + "filePath": "../../node_modules/@shopify/shopify-api/lib/billing/types.d.ts", + "syntaxKind": "PropertySignature", + "name": "replacementBehavior", + "value": "BillingReplacementBehavior", + "description": "The behavior to use when replacing an existing subscription with a new one.", + "isOptional": true }, { - "filePath": "../../node_modules/@shopify/polaris/build/ts/src/types.d.ts", - "name": "Dash", - "value": 189 + "filePath": "../../node_modules/@shopify/shopify-api/lib/billing/types.d.ts", + "syntaxKind": "PropertySignature", + "name": "amount", + "value": "number", + "description": "Amount to charge for this plan." }, { - "filePath": "../../node_modules/@shopify/polaris/build/ts/src/types.d.ts", - "name": "Period", - "value": 190 - }, + "filePath": "../../node_modules/@shopify/shopify-api/lib/billing/types.d.ts", + "syntaxKind": "PropertySignature", + "name": "currencyCode", + "value": "string", + "description": "Currency code for this plan." + } + ], + "value": "export interface BillingConfigUsagePlan extends BillingConfigPlan {\n /**\n * Interval for this plan.\n *\n * Must be set to `Usage`.\n */\n interval: BillingInterval.Usage;\n /**\n * Usage terms for this plan.\n */\n usageTerms: string;\n /**\n * How many trial days to give before charging for this plan.\n */\n trialDays?: number;\n /**\n * The behavior to use when replacing an existing subscription with a new one.\n */\n replacementBehavior?: BillingReplacementBehavior;\n}" + }, + "LogFunction": { + "filePath": "../../node_modules/@shopify/shopify-api/lib/base-types.d.ts", + "name": "LogFunction", + "description": "A function used by the library to log events related to Shopify.", + "params": [ { - "filePath": "../../node_modules/@shopify/polaris/build/ts/src/types.d.ts", - "name": "ForwardSlash", - "value": 191 + "name": "severity", + "description": "", + "value": "LogSeverity", + "filePath": "../../node_modules/@shopify/shopify-api/lib/base-types.d.ts" }, { - "filePath": "../../node_modules/@shopify/polaris/build/ts/src/types.d.ts", - "name": "GraveAccent", - "value": 192 - }, + "name": "msg", + "description": "", + "value": "string", + "filePath": "../../node_modules/@shopify/shopify-api/lib/base-types.d.ts" + } + ], + "returns": { + "filePath": "../../node_modules/@shopify/shopify-api/lib/base-types.d.ts", + "description": "", + "name": "void", + "value": "void" + }, + "value": "export type LogFunction = (severity: LogSeverity, msg: string) => void;" + }, + "LogSeverity": { + "filePath": "../../node_modules/@shopify/shopify-api/lib/types.d.ts", + "syntaxKind": "EnumDeclaration", + "name": "LogSeverity", + "value": "export declare enum LogSeverity {\n Error = 0,\n Warning = 1,\n Info = 2,\n Debug = 3\n}", + "members": [ { - "filePath": "../../node_modules/@shopify/polaris/build/ts/src/types.d.ts", - "name": "OpenBracket", - "value": 219 + "filePath": "../../node_modules/@shopify/shopify-api/lib/types.d.ts", + "name": "Error", + "value": 0 }, { - "filePath": "../../node_modules/@shopify/polaris/build/ts/src/types.d.ts", - "name": "BackSlash", - "value": 220 + "filePath": "../../node_modules/@shopify/shopify-api/lib/types.d.ts", + "name": "Warning", + "value": 1 }, { - "filePath": "../../node_modules/@shopify/polaris/build/ts/src/types.d.ts", - "name": "CloseBracket", - "value": 221 + "filePath": "../../node_modules/@shopify/shopify-api/lib/types.d.ts", + "name": "Info", + "value": 2 }, { - "filePath": "../../node_modules/@shopify/polaris/build/ts/src/types.d.ts", - "name": "SingleQuote", - "value": 222 + "filePath": "../../node_modules/@shopify/shopify-api/lib/types.d.ts", + "name": "Debug", + "value": 3 } ] }, @@ -14981,124 +15377,6 @@ } ] }, - "Readonly": { - "filePath": "../../node_modules/@shopify/polaris/build/ts/src/components/TextField/TextField.d.ts", - "name": "Readonly", - "description": "", - "members": [ - { - "filePath": "../../node_modules/@shopify/polaris/build/ts/src/components/TextField/TextField.d.ts", - "syntaxKind": "PropertySignature", - "name": "readonly", - "value": "true", - "description": "", - "isOptional": true - } - ], - "value": "interface Readonly {\n readonly?: true;\n}" - }, - "ApiClientFetch": { - "filePath": "../../node_modules/@shopify/shopify-api/node_modules/@shopify/graphql-client/dist/graphql-client.d.ts", - "name": "ApiClientFetch", - "description": "", - "params": [ - { - "name": "params", - "description": "", - "value": "ApiClientRequestParams", - "filePath": "../../node_modules/@shopify/shopify-api/node_modules/@shopify/graphql-client/dist/graphql-client.d.ts" - } - ], - "returns": { - "filePath": "../../node_modules/@shopify/shopify-api/node_modules/@shopify/graphql-client/dist/graphql-client.d.ts", - "description": "", - "name": "interface Promise {\n /**\n * Attaches callbacks for the resolution and/or rejection of the Promise.\n * @param onfulfilled The callback to execute when the Promise is resolved.\n * @param onrejected The callback to execute when the Promise is rejected.\n * @returns A Promise for the completion of which ever callback is executed.\n */\n then(onfulfilled?: ((value: T) => TResult1 | PromiseLike) | undefined | null, onrejected?: ((reason: any) => TResult2 | PromiseLike) | undefined | null): Promise;\n\n /**\n * Attaches a callback for only the rejection of the Promise.\n * @param onrejected The callback to execute when the Promise is rejected.\n * @returns A Promise for the completion of the callback.\n */\n catch(onrejected?: ((reason: any) => TResult | PromiseLike) | undefined | null): Promise;\n}, interface Promise {}, Promise: PromiseConstructor, interface Promise {\n readonly [Symbol.toStringTag]: string;\n}, interface Promise {\n /**\n * Attaches a callback that is invoked when the Promise is settled (fulfilled or rejected). The\n * resolved value cannot be modified from the callback.\n * @param onfinally The callback to execute when the Promise is settled (fulfilled or rejected).\n * @returns A Promise for the completion of the callback.\n */\n finally(onfinally?: (() => void) | undefined | null): Promise;\n}", - "value": "interface Promise {\n /**\n * Attaches callbacks for the resolution and/or rejection of the Promise.\n * @param onfulfilled The callback to execute when the Promise is resolved.\n * @param onrejected The callback to execute when the Promise is rejected.\n * @returns A Promise for the completion of which ever callback is executed.\n */\n then(onfulfilled?: ((value: T) => TResult1 | PromiseLike) | undefined | null, onrejected?: ((reason: any) => TResult2 | PromiseLike) | undefined | null): Promise;\n\n /**\n * Attaches a callback for only the rejection of the Promise.\n * @param onrejected The callback to execute when the Promise is rejected.\n * @returns A Promise for the completion of the callback.\n */\n catch(onrejected?: ((reason: any) => TResult | PromiseLike) | undefined | null): Promise;\n}, interface Promise {}, Promise: PromiseConstructor, interface Promise {\n readonly [Symbol.toStringTag]: string;\n}, interface Promise {\n /**\n * Attaches a callback that is invoked when the Promise is settled (fulfilled or rejected). The\n * resolved value cannot be modified from the callback.\n * @param onfinally The callback to execute when the Promise is settled (fulfilled or rejected).\n * @returns A Promise for the completion of the callback.\n */\n finally(onfinally?: (() => void) | undefined | null): Promise;\n}" - }, - "value": "type ApiClientFetch = (...params: ApiClientRequestParams) => Promise>>>;" - }, - "ApiClientRequestParams": { - "filePath": "../../node_modules/@shopify/shopify-api/node_modules/@shopify/graphql-client/dist/graphql-client.d.ts", - "syntaxKind": "TypeAliasDeclaration", - "name": "ApiClientRequestParams", - "value": "[\n operation: Operation,\n options?: ApiClientRequestOptions\n]", - "description": "", - "members": [ - { - "name": "[0]", - "value": "operation: Operation", - "filePath": "../../node_modules/@shopify/shopify-api/node_modules/@shopify/graphql-client/dist/graphql-client.d.ts" - }, - { - "name": "[1]", - "value": "options?: ApiClientRequestOptions", - "filePath": "../../node_modules/@shopify/shopify-api/node_modules/@shopify/graphql-client/dist/graphql-client.d.ts" - } - ] - }, - "ApiClientRequestOptions": { - "filePath": "../../node_modules/@shopify/shopify-api/node_modules/@shopify/graphql-client/dist/graphql-client.d.ts", - "syntaxKind": "TypeAliasDeclaration", - "name": "ApiClientRequestOptions", - "value": "{\n apiVersion?: string;\n headers?: Headers;\n retries?: number;\n} & (Operation extends keyof Operations ? OperationVariables : {\n variables?: Record;\n})", - "description": "" - }, - "Headers": { - "filePath": "../../node_modules/@shopify/shopify-api/node_modules/@shopify/graphql-client/dist/graphql-client.d.ts", - "syntaxKind": "TypeAliasDeclaration", - "name": "Headers", - "value": "Record", - "description": "", - "members": [] - }, - "OperationVariables": { - "filePath": "../../node_modules/@shopify/shopify-api/node_modules/@shopify/graphql-client/dist/graphql-client.d.ts", - "syntaxKind": "TypeAliasDeclaration", - "name": "OperationVariables", - "value": "Operations[Operation][\"variables\"] extends Record ? Record : {\n variables?: {\n [k in keyof Operations[Operation][\"variables\"]]: UnpackedInputMaybe;\n };\n}", - "description": "" - }, - "UnpackedInputMaybe": { - "filePath": "../../node_modules/@shopify/shopify-api/node_modules/@shopify/graphql-client/dist/graphql-client.d.ts", - "syntaxKind": "TypeAliasDeclaration", - "name": "UnpackedInputMaybe", - "value": "InputType extends InputMaybe ? InputMaybe> : UnpackedInput", - "description": "" - }, - "InputMaybe": { - "filePath": "../../node_modules/@shopify/shopify-api/node_modules/@shopify/graphql-client/dist/graphql-client.d.ts", - "syntaxKind": "TypeAliasDeclaration", - "name": "InputMaybe", - "value": "never", - "description": "" - }, - "UnpackedInput": { - "filePath": "../../node_modules/@shopify/shopify-api/node_modules/@shopify/graphql-client/dist/graphql-client.d.ts", - "syntaxKind": "TypeAliasDeclaration", - "name": "UnpackedInput", - "value": "\"input\" extends keyof InputType ? InputType[\"input\"] : InputType", - "description": "" - }, - "ApiClientRequest": { - "filePath": "../../node_modules/@shopify/shopify-api/node_modules/@shopify/graphql-client/dist/graphql-client.d.ts", - "name": "ApiClientRequest", - "description": "", - "params": [ - { - "name": "params", - "description": "", - "value": "ApiClientRequestParams", - "filePath": "../../node_modules/@shopify/shopify-api/node_modules/@shopify/graphql-client/dist/graphql-client.d.ts" - } - ], - "returns": { - "filePath": "../../node_modules/@shopify/shopify-api/node_modules/@shopify/graphql-client/dist/graphql-client.d.ts", - "description": "", - "name": "interface Promise {\n /**\n * Attaches callbacks for the resolution and/or rejection of the Promise.\n * @param onfulfilled The callback to execute when the Promise is resolved.\n * @param onrejected The callback to execute when the Promise is rejected.\n * @returns A Promise for the completion of which ever callback is executed.\n */\n then(onfulfilled?: ((value: T) => TResult1 | PromiseLike) | undefined | null, onrejected?: ((reason: any) => TResult2 | PromiseLike) | undefined | null): Promise;\n\n /**\n * Attaches a callback for only the rejection of the Promise.\n * @param onrejected The callback to execute when the Promise is rejected.\n * @returns A Promise for the completion of the callback.\n */\n catch(onrejected?: ((reason: any) => TResult | PromiseLike) | undefined | null): Promise;\n}, interface Promise {}, Promise: PromiseConstructor, interface Promise {\n readonly [Symbol.toStringTag]: string;\n}, interface Promise {\n /**\n * Attaches a callback that is invoked when the Promise is settled (fulfilled or rejected). The\n * resolved value cannot be modified from the callback.\n * @param onfinally The callback to execute when the Promise is settled (fulfilled or rejected).\n * @returns A Promise for the completion of the callback.\n */\n finally(onfinally?: (() => void) | undefined | null): Promise;\n}", - "value": "interface Promise {\n /**\n * Attaches callbacks for the resolution and/or rejection of the Promise.\n * @param onfulfilled The callback to execute when the Promise is resolved.\n * @param onrejected The callback to execute when the Promise is rejected.\n * @returns A Promise for the completion of which ever callback is executed.\n */\n then(onfulfilled?: ((value: T) => TResult1 | PromiseLike) | undefined | null, onrejected?: ((reason: any) => TResult2 | PromiseLike) | undefined | null): Promise;\n\n /**\n * Attaches a callback for only the rejection of the Promise.\n * @param onrejected The callback to execute when the Promise is rejected.\n * @returns A Promise for the completion of the callback.\n */\n catch(onrejected?: ((reason: any) => TResult | PromiseLike) | undefined | null): Promise;\n}, interface Promise {}, Promise: PromiseConstructor, interface Promise {\n readonly [Symbol.toStringTag]: string;\n}, interface Promise {\n /**\n * Attaches a callback that is invoked when the Promise is settled (fulfilled or rejected). The\n * resolved value cannot be modified from the callback.\n * @param onfinally The callback to execute when the Promise is settled (fulfilled or rejected).\n * @returns A Promise for the completion of the callback.\n */\n finally(onfinally?: (() => void) | undefined | null): Promise;\n}" - }, - "value": "type ApiClientRequest = (...params: ApiClientRequestParams) => Promise : TData>>;" - }, "GraphqlParams": { "filePath": "../../node_modules/@shopify/shopify-api/lib/clients/types.d.ts", "syntaxKind": "TypeAliasDeclaration", @@ -15135,118 +15413,31 @@ "description": "", "members": [ { - "filePath": "../../node_modules/@shopify/shopify-api/lib/clients/types.d.ts", - "syntaxKind": "PropertySignature", - "name": "variables", - "value": "ApiClientRequestOptions[\"variables\"]", - "description": "The variables to include in the operation.", - "isOptional": true - }, - { - "filePath": "../../node_modules/@shopify/shopify-api/lib/clients/types.d.ts", - "syntaxKind": "PropertySignature", - "name": "headers", - "value": "Record", - "description": "Additional headers to be sent with the request.", - "isOptional": true - }, - { - "filePath": "../../node_modules/@shopify/shopify-api/lib/clients/types.d.ts", - "syntaxKind": "PropertySignature", - "name": "retries", - "value": "number", - "description": "The maximum number of times to retry the request if it fails with a throttling or server error.", - "isOptional": true - } - ], - "value": "export interface GraphqlQueryOptions {\n /**\n * The variables to include in the operation.\n */\n variables?: ApiClientRequestOptions['variables'];\n /**\n * Additional headers to be sent with the request.\n */\n headers?: Record;\n /**\n * The maximum number of times to retry the request if it fails with a throttling or server error.\n */\n retries?: number;\n}" - }, - "ClientResponse": { - "filePath": "../../node_modules/@shopify/shopify-api/node_modules/@shopify/graphql-client/dist/graphql-client.d.ts", - "name": "ClientResponse", - "description": "", - "members": [ - { - "filePath": "../../node_modules/@shopify/shopify-api/node_modules/@shopify/graphql-client/dist/graphql-client.d.ts", - "syntaxKind": "PropertySignature", - "name": "errors", - "value": "ResponseErrors", - "description": "", - "isOptional": true - }, - { - "filePath": "../../node_modules/@shopify/shopify-api/node_modules/@shopify/graphql-client/dist/graphql-client.d.ts", - "syntaxKind": "PropertySignature", - "name": "data", - "value": "TData", - "description": "", - "isOptional": true - }, - { - "filePath": "../../node_modules/@shopify/shopify-api/node_modules/@shopify/graphql-client/dist/graphql-client.d.ts", - "syntaxKind": "PropertySignature", - "name": "extensions", - "value": "GQLExtensions", - "description": "", - "isOptional": true - } - ], - "value": "interface ClientResponse extends FetchResponseBody {\n errors?: ResponseErrors;\n}" - }, - "ResponseErrors": { - "filePath": "../../node_modules/@shopify/shopify-api/node_modules/@shopify/graphql-client/dist/graphql-client.d.ts", - "name": "ResponseErrors", - "description": "", - "members": [ - { - "filePath": "../../node_modules/@shopify/shopify-api/node_modules/@shopify/graphql-client/dist/graphql-client.d.ts", - "syntaxKind": "PropertySignature", - "name": "networkStatusCode", - "value": "number", - "description": "", - "isOptional": true - }, - { - "filePath": "../../node_modules/@shopify/shopify-api/node_modules/@shopify/graphql-client/dist/graphql-client.d.ts", - "syntaxKind": "PropertySignature", - "name": "message", - "value": "string", - "description": "", + "filePath": "../../node_modules/@shopify/shopify-api/lib/clients/types.d.ts", + "syntaxKind": "PropertySignature", + "name": "variables", + "value": "ApiClientRequestOptions[\"variables\"]", + "description": "The variables to include in the operation.", "isOptional": true }, { - "filePath": "../../node_modules/@shopify/shopify-api/node_modules/@shopify/graphql-client/dist/graphql-client.d.ts", + "filePath": "../../node_modules/@shopify/shopify-api/lib/clients/types.d.ts", "syntaxKind": "PropertySignature", - "name": "graphQLErrors", - "value": "any[]", - "description": "", + "name": "headers", + "value": "Record", + "description": "Additional headers to be sent with the request.", "isOptional": true }, { - "filePath": "../../node_modules/@shopify/shopify-api/node_modules/@shopify/graphql-client/dist/graphql-client.d.ts", + "filePath": "../../node_modules/@shopify/shopify-api/lib/clients/types.d.ts", "syntaxKind": "PropertySignature", - "name": "response", - "value": "Response", - "description": "", + "name": "retries", + "value": "number", + "description": "The maximum number of times to retry the request if it fails with a throttling or server error.", "isOptional": true } ], - "value": "interface ResponseErrors {\n networkStatusCode?: number;\n message?: string;\n graphQLErrors?: any[];\n response?: Response;\n}" - }, - "GQLExtensions": { - "filePath": "../../node_modules/@shopify/shopify-api/node_modules/@shopify/graphql-client/dist/graphql-client.d.ts", - "syntaxKind": "TypeAliasDeclaration", - "name": "GQLExtensions", - "value": "Record", - "description": "", - "members": [] - }, - "ReturnData": { - "filePath": "../../node_modules/@shopify/shopify-api/node_modules/@shopify/graphql-client/dist/graphql-client.d.ts", - "syntaxKind": "TypeAliasDeclaration", - "name": "ReturnData", - "value": "Operation extends keyof Operations ? Operations[Operation][\"return\"] : any", - "description": "" + "value": "export interface GraphqlQueryOptions {\n /**\n * The variables to include in the operation.\n */\n variables?: ApiClientRequestOptions['variables'];\n /**\n * Additional headers to be sent with the request.\n */\n headers?: Record;\n /**\n * The maximum number of times to retry the request if it fails with a throttling or server error.\n */\n retries?: number;\n}" }, "StorefrontClient": { "filePath": "../../node_modules/@shopify/shopify-api/lib/clients/storefront/client.d.ts", @@ -15295,45 +15486,15 @@ "filePath": "../../node_modules/@shopify/shopify-api/node_modules/@shopify/storefront-api-client/dist/storefront-api-client.d.ts", "syntaxKind": "TypeAliasDeclaration", "name": "StorefrontApiClient", - "value": "ApiClient", - "description": "", - "members": [ - { - "filePath": "../../node_modules/@shopify/shopify-api/node_modules/@shopify/storefront-api-client/dist/storefront-api-client.d.ts", - "syntaxKind": "PropertySignature", - "name": "config", - "value": "Readonly", - "description": "" - }, - { - "filePath": "../../node_modules/@shopify/shopify-api/node_modules/@shopify/storefront-api-client/dist/storefront-api-client.d.ts", - "syntaxKind": "PropertySignature", - "name": "getHeaders", - "value": "(headers?: Headers) => Headers", - "description": "" - }, - { - "filePath": "../../node_modules/@shopify/shopify-api/node_modules/@shopify/storefront-api-client/dist/storefront-api-client.d.ts", - "syntaxKind": "PropertySignature", - "name": "getApiUrl", - "value": "(apiVersion?: string) => string", - "description": "" - }, - { - "filePath": "../../node_modules/@shopify/shopify-api/node_modules/@shopify/storefront-api-client/dist/storefront-api-client.d.ts", - "syntaxKind": "PropertySignature", - "name": "fetch", - "value": "ApiClientFetch", - "description": "" - }, - { - "filePath": "../../node_modules/@shopify/shopify-api/node_modules/@shopify/storefront-api-client/dist/storefront-api-client.d.ts", - "syntaxKind": "PropertySignature", - "name": "request", - "value": "ApiClientRequest", - "description": "" - } - ] + "value": "ApiClient & {\n requestStream: ApiClientRequestStream;\n}", + "description": "" + }, + "StorefrontApiClientConfig": { + "filePath": "../../node_modules/@shopify/shopify-api/node_modules/@shopify/storefront-api-client/dist/storefront-api-client.d.ts", + "syntaxKind": "TypeAliasDeclaration", + "name": "StorefrontApiClientConfig", + "value": "ApiClientConfig & {\n clientName?: string;\n} & ({\n publicAccessToken?: never;\n privateAccessToken: string;\n} | {\n publicAccessToken: string;\n privateAccessToken?: never;\n})", + "description": "" }, "GraphqlProxy": { "filePath": "../../node_modules/@shopify/shopify-api/lib/clients/graphql_proxy/types.d.ts", @@ -17143,26 +17304,227 @@ "title": "~/shopify.server.ts" }, { - "code": "import { addDocumentResponseHeaders } from \"~/shopify.server\";\n\nexport default function handleRequest(\n request: Request,\n responseStatusCode: number,\n responseHeaders: Headers,\n remixContext: EntryContext\n) {\n const markup = renderToString(\n \n );\n\n responseHeaders.set(\"Content-Type\", \"text/html\");\n addDocumentResponseHeaders(request, responseHeaders);\n\n return new Response(\"\" + markup, {\n status: responseStatusCode,\n headers: responseHeaders,\n });\n}", - "title": "entry.server.tsx" + "code": "import { addDocumentResponseHeaders } from \"~/shopify.server\";\n\nexport default function handleRequest(\n request: Request,\n responseStatusCode: number,\n responseHeaders: Headers,\n remixContext: EntryContext\n) {\n const markup = renderToString(\n \n );\n\n responseHeaders.set(\"Content-Type\", \"text/html\");\n addDocumentResponseHeaders(request, responseHeaders);\n\n return new Response(\"\" + markup, {\n status: responseStatusCode,\n headers: responseHeaders,\n });\n}", + "title": "entry.server.tsx" + } + ] + } + ] + }, + { + "filePath": "src/server/types.ts", + "syntaxKind": "PropertySignature", + "name": "registerWebhooks", + "value": "RegisterWebhooks", + "description": "Register webhook topics for a store using the given session. Most likely you want to use this in combination with the afterAuth hook.", + "examples": [ + { + "title": "Registering webhooks after install", + "description": "Trigger the registration to create the webhook subscriptions after a merchant installs your app using the `afterAuth` hook. Learn more about [subscribing to webhooks.](/docs/api/shopify-app-remix/v1/guide-webhooks)", + "tabs": [ + { + "code": "import { DeliveryMethod, shopifyApp } from \"@shopify/shopify-app-remix/server\";\n\nconst shopify = shopifyApp({\n hooks: {\n afterAuth: async ({ session }) => {\n shopify.registerWebhooks({ session });\n }\n },\n webhooks: {\n APP_UNINSTALLED: {\n deliveryMethod: DeliveryMethod.Http,\n callbackUrl: \"/webhooks\",\n },\n },\n // ...etc\n});", + "title": "/app/shopify.server.ts" + } + ] + } + ] + }, + { + "filePath": "src/server/types.ts", + "syntaxKind": "PropertySignature", + "name": "authenticate", + "value": "Authenticate", + "description": "Ways to authenticate requests from different surfaces across Shopify.", + "examples": [ + { + "title": "Authenticate Shopify requests", + "description": "Use the functions in `authenticate` to validate requests coming from Shopify.", + "tabs": [ + { + "code": "import { LATEST_API_VERSION, shopifyApp } from \"@shopify/shopify-app-remix/server\";\nimport { restResources } from \"@shopify/shopify-api/rest/admin/2023-04\";\n\nconst shopify = shopifyApp({\n restResources,\n // ...etc\n});\nexport default shopify;", + "title": "/app/shopify.server.ts" + }, + { + "code": "import { LoaderFunctionArgs, json } from \"@remix-run/node\";\nimport shopify from \"../../shopify.server\";\n\nexport async function loader({ request }: LoaderFunctionArgs) {\n const {admin, session, sessionToken, billing} = shopify.authenticate.admin(request);\n\n return json(await admin.rest.resources.Product.count({ session }));\n}", + "title": "/app/routes/**\\/*.jsx" + } + ] + } + ] + }, + { + "filePath": "src/server/types.ts", + "syntaxKind": "PropertySignature", + "name": "unauthenticated", + "value": "Unauthenticated>", + "description": "Ways to get Contexts from requests that do not originate from Shopify.", + "examples": [ + { + "title": "Using unauthenticated contexts", + "description": "Create contexts for requests that don't come from Shopify.", + "tabs": [ + { + "code": "import { LATEST_API_VERSION, shopifyApp } from \"@shopify/shopify-app-remix/server\";\nimport { restResources } from \"@shopify/shopify-api/rest/admin/2023-04\";\n\nconst shopify = shopifyApp({\n restResources,\n // ...etc\n});\nexport default shopify;", + "title": "/app/shopify.server.ts" + }, + { + "code": "import { LoaderFunctionArgs, json } from \"@remix-run/node\";\nimport { authenticateExternal } from \"~/helpers/authenticate\"\nimport shopify from \"../../shopify.server\";\n\nexport async function loader({ request }: LoaderFunctionArgs) {\n const shop = await authenticateExternal(request)\n const {admin} = await shopify.unauthenticated.admin(shop);\n\n return json(await admin.rest.resources.Product.count({ session }));\n}", + "title": "/app/routes/**\\/*.jsx" + } + ] + } + ] + } + ], + "value": "export interface ShopifyAppBase {\n /**\n * The `SessionStorage` instance you passed in as a config option.\n *\n * @example\n * Storing sessions with Prisma.\n * Import the `@shopify/shopify-app-session-storage-prisma` package to store sessions in your Prisma database.\n * ```ts\n * // /app/shopify.server.ts\n * import { shopifyApp } from \"@shopify/shopify-app-remix/server\";\n * import { PrismaSessionStorage } from \"@shopify/shopify-app-session-storage-prisma\";\n * import prisma from \"~/db.server\";\n *\n * const shopify = shopifyApp({\n * sessionStorage: new PrismaSessionStorage(prisma),\n * // ...etc\n * })\n *\n * // shopify.sessionStorage is an instance of PrismaSessionStorage\n * ```\n */\n sessionStorage: SessionStorageType;\n\n /**\n * Adds the required Content Security Policy headers for Shopify apps to the given Headers object.\n *\n * {@link https://shopify.dev/docs/apps/store/security/iframe-protection}\n *\n * @example\n * Return headers on all requests.\n * Add headers to all HTML requests by calling `shopify.addDocumentResponseHeaders` in `entry.server.tsx`.\n *\n * ```\n * // ~/shopify.server.ts\n * import { shopifyApp } from \"@shopify/shopify-app-remix/server\";\n *\n * const shopify = shopifyApp({\n * // ...etc\n * });\n * export default shopify;\n * export const addDocumentResponseheaders = shopify.addDocumentResponseheaders;\n * ```\n *\n * ```ts\n * // entry.server.tsx\n * import { addDocumentResponseHeaders } from \"~/shopify.server\";\n *\n * export default function handleRequest(\n * request: Request,\n * responseStatusCode: number,\n * responseHeaders: Headers,\n * remixContext: EntryContext\n * ) {\n * const markup = renderToString(\n * \n * );\n *\n * responseHeaders.set(\"Content-Type\", \"text/html\");\n * addDocumentResponseHeaders(request, responseHeaders);\n *\n * return new Response(\"\" + markup, {\n * status: responseStatusCode,\n * headers: responseHeaders,\n * });\n * }\n * ```\n */\n addDocumentResponseHeaders: AddDocumentResponseHeaders;\n\n /**\n * Register webhook topics for a store using the given session. Most likely you want to use this in combination with the afterAuth hook.\n *\n * @example\n * Registering webhooks after install\n * Trigger the registration to create the webhook subscriptions after a merchant installs your app using the `afterAuth` hook. Learn more about [subscribing to webhooks.](/docs/api/shopify-app-remix/v1/guide-webhooks)\n * ```ts\n * // /app/shopify.server.ts\n * import { DeliveryMethod, shopifyApp } from \"@shopify/shopify-app-remix/server\";\n *\n * const shopify = shopifyApp({\n * hooks: {\n * afterAuth: async ({ session }) => {\n * shopify.registerWebhooks({ session });\n * }\n * },\n * webhooks: {\n * APP_UNINSTALLED: {\n * deliveryMethod: DeliveryMethod.Http,\n * callbackUrl: \"/webhooks\",\n * },\n * },\n * // ...etc\n * });\n * ```\n */\n registerWebhooks: RegisterWebhooks;\n\n /**\n * Ways to authenticate requests from different surfaces across Shopify.\n *\n * @example\n * Authenticate Shopify requests.\n * Use the functions in `authenticate` to validate requests coming from Shopify.\n * ```ts\n * // /app/shopify.server.ts\n * import { LATEST_API_VERSION, shopifyApp } from \"@shopify/shopify-app-remix/server\";\n * import { restResources } from \"@shopify/shopify-api/rest/admin/2023-04\";\n *\n * const shopify = shopifyApp({\n * restResources,\n * // ...etc\n * });\n * export default shopify;\n * ```\n * ```ts\n * // /app/routes/**\\/*.jsx\n * import { LoaderFunctionArgs, json } from \"@remix-run/node\";\n * import shopify from \"../../shopify.server\";\n *\n * export async function loader({ request }: LoaderFunctionArgs) {\n * const {admin, session, sessionToken, billing} = shopify.authenticate.admin(request);\n *\n * return json(await admin.rest.resources.Product.count({ session }));\n * }\n * ```\n */\n authenticate: Authenticate;\n\n /**\n * Ways to get Contexts from requests that do not originate from Shopify.\n *\n * @example\n * Using unauthenticated contexts.\n * Create contexts for requests that don't come from Shopify.\n * ```ts\n * // /app/shopify.server.ts\n * import { LATEST_API_VERSION, shopifyApp } from \"@shopify/shopify-app-remix/server\";\n * import { restResources } from \"@shopify/shopify-api/rest/admin/2023-04\";\n *\n * const shopify = shopifyApp({\n * restResources,\n * // ...etc\n * });\n * export default shopify;\n * ```\n * ```ts\n * // /app/routes/**\\/*.jsx\n * import { LoaderFunctionArgs, json } from \"@remix-run/node\";\n * import { authenticateExternal } from \"~/helpers/authenticate\"\n * import shopify from \"../../shopify.server\";\n *\n * export async function loader({ request }: LoaderFunctionArgs) {\n * const shop = await authenticateExternal(request)\n * const {admin} = await shopify.unauthenticated.admin(shop);\n *\n * return json(await admin.rest.resources.Product.count({ session }));\n * }\n * ```\n */\n unauthenticated: Unauthenticated>;\n}" + }, + "ShopifyAppLogin": { + "filePath": "src/server/types.ts", + "name": "ShopifyAppLogin", + "description": "", + "members": [ + { + "filePath": "src/server/types.ts", + "syntaxKind": "PropertySignature", + "name": "login", + "value": "Login", + "description": "Log a merchant in, and redirect them to the app root. Will redirect the merchant to authentication if a shop is present in the URL search parameters or form data.\n\nThis function won't be present when the `distribution` config option is set to `AppDistribution.ShopifyAdmin`, because Admin apps aren't allowed to show a login page.", + "examples": [ + { + "title": "Creating a login page", + "description": "Use `shopify.login` to create a login form, in a route that can handle GET and POST requests.", + "tabs": [ + { + "code": "import { LATEST_API_VERSION, shopifyApp } from \"@shopify/shopify-app-remix/server\";\n\nconst shopify = shopifyApp({\n // ...etc\n});\nexport default shopify;", + "title": "/app/shopify.server.ts" + }, + { + "code": "import shopify from \"../../shopify.server\";\n\nexport async function loader({ request }: LoaderFunctionArgs) {\n const errors = shopify.login(request);\n\n return json(errors);\n}\n\nexport async function action({ request }: ActionFunctionArgs) {\n const errors = shopify.login(request);\n\n return json(errors);\n}\n\nexport default function Auth() {\n const actionData = useActionData();\n const [shop, setShop] = useState(\"\");\n\n return (\n \n \n
\n \n \n Login\n \n \n \n \n
\n
\n
\n );\n}", + "title": "/app/routes/auth/login.tsx" + } + ] + } + ] + } + ], + "value": "interface ShopifyAppLogin {\n /**\n * Log a merchant in, and redirect them to the app root. Will redirect the merchant to authentication if a shop is\n * present in the URL search parameters or form data.\n *\n * This function won't be present when the `distribution` config option is set to `AppDistribution.ShopifyAdmin`,\n * because Admin apps aren't allowed to show a login page.\n *\n * @example\n * Creating a login page.\n * Use `shopify.login` to create a login form, in a route that can handle GET and POST requests.\n * ```ts\n * // /app/shopify.server.ts\n * import { LATEST_API_VERSION, shopifyApp } from \"@shopify/shopify-app-remix/server\";\n *\n * const shopify = shopifyApp({\n * // ...etc\n * });\n * export default shopify;\n * ```\n * ```ts\n * // /app/routes/auth/login.tsx\n * import shopify from \"../../shopify.server\";\n *\n * export async function loader({ request }: LoaderFunctionArgs) {\n * const errors = shopify.login(request);\n *\n * return json(errors);\n * }\n *\n * export async function action({ request }: ActionFunctionArgs) {\n * const errors = shopify.login(request);\n *\n * return json(errors);\n * }\n *\n * export default function Auth() {\n * const actionData = useActionData();\n * const [shop, setShop] = useState(\"\");\n *\n * return (\n * \n * \n *
\n * \n * \n * Login\n * \n * \n * \n * \n *
\n *
\n *
\n * );\n * }\n * ```\n */\n login: Login;\n}" + }, + "Login": { + "filePath": "src/server/types.ts", + "name": "Login", + "description": "", + "params": [ + { + "name": "request", + "description": "", + "value": "Request", + "filePath": "src/server/types.ts" + } + ], + "returns": { + "filePath": "src/server/types.ts", + "description": "", + "name": "Promise", + "value": "Promise" + }, + "value": "type Login = (request: Request) => Promise;" + }, + "LoginError": { + "filePath": "src/server/types.ts", + "name": "LoginError", + "description": "", + "members": [ + { + "filePath": "src/server/types.ts", + "syntaxKind": "PropertySignature", + "name": "shop", + "value": "LoginErrorType", + "description": "", + "isOptional": true + } + ], + "value": "export interface LoginError {\n shop?: LoginErrorType;\n}" + }, + "LoginErrorType": { + "filePath": "src/server/types.ts", + "syntaxKind": "EnumDeclaration", + "name": "LoginErrorType", + "value": "export enum LoginErrorType {\n MissingShop = 'MISSING_SHOP',\n InvalidShop = 'INVALID_SHOP',\n}", + "members": [ + { + "filePath": "src/server/types.ts", + "name": "MissingShop", + "value": "MISSING_SHOP" + }, + { + "filePath": "src/server/types.ts", + "name": "InvalidShop", + "value": "INVALID_SHOP" + } + ] + }, + "AppStoreApp": { + "filePath": "src/server/types.ts", + "syntaxKind": "TypeAliasDeclaration", + "name": "AppStoreApp", + "value": "ShopifyAppBase & ShopifyAppLogin", + "description": "" + }, + "AppConfigArg": { + "filePath": "src/server/config-types.ts", + "name": "AppConfigArg", + "description": "", + "members": [ + { + "filePath": "src/server/config-types.ts", + "syntaxKind": "PropertySignature", + "name": "appUrl", + "value": "string", + "description": "The URL your app is running on.\n\nThe `@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." + }, + { + "filePath": "src/server/config-types.ts", + "syntaxKind": "PropertySignature", + "name": "sessionStorage", + "value": "Storage", + "description": "An adaptor for storing sessions in your database of choice.\n\nShopify provides multiple session storage adaptors and you can create your own.\n\n\n\n\n", + "examples": [ + { + "title": "Storing sessions with Prisma", + "description": "Add the `@shopify/shopify-app-session-storage-prisma` package to use the Prisma session storage.", + "tabs": [ + { + "code": "import { shopifyApp } from \"@shopify/shopify-app-remix/server\";\nimport { PrismaSessionStorage } from \"@shopify/shopify-app-session-storage-prisma\";\n\nimport prisma from \"~/db.server\";\n\nconst shopify = shopifyApp({\n // ... etc\n sessionStorage: new PrismaSessionStorage(prisma),\n});\nexport default shopify;", + "title": "Example" } ] } ] }, { - "filePath": "src/server/types.ts", + "filePath": "src/server/config-types.ts", "syntaxKind": "PropertySignature", - "name": "registerWebhooks", - "value": "RegisterWebhooks", - "description": "Register webhook topics for a store using the given session. Most likely you want to use this in combination with the afterAuth hook.", + "name": "useOnlineTokens", + "value": "boolean", + "description": "Whether your app use online or offline tokens.\n\nIf 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.\n\n\n\n\n", + "isOptional": true, + "defaultValue": "`false`" + }, + { + "filePath": "src/server/config-types.ts", + "syntaxKind": "PropertySignature", + "name": "webhooks", + "value": "WebhookConfig", + "description": "The config for the webhook topics your app would like to subscribe to.\n\n\n\n\n\n\n\nThis can be in used in conjunction with the afterAuth hook to register webhook topics when a user installs your app. Or you can use this function in other processes such as background jobs.", + "isOptional": true, "examples": [ { - "title": "Registering webhooks after install", - "description": "Trigger the registration to create the webhook subscriptions after a merchant installs your app using the `afterAuth` hook. Learn more about [subscribing to webhooks.](/docs/api/shopify-app-remix/v1/guide-webhooks)", + "title": "Registering for a webhook when a merchant uninstalls your app", + "description": "", "tabs": [ { - "code": "import { DeliveryMethod, shopifyApp } from \"@shopify/shopify-app-remix/server\";\n\nconst shopify = shopifyApp({\n hooks: {\n afterAuth: async ({ session }) => {\n shopify.registerWebhooks({ session });\n }\n },\n webhooks: {\n APP_UNINSTALLED: {\n deliveryMethod: DeliveryMethod.Http,\n callbackUrl: \"/webhooks\",\n },\n },\n // ...etc\n});", + "code": "import { DeliveryMethod, shopifyApp } from \"@shopify/shopify-app-remix/server\";\n\nconst shopify = shopifyApp({\n webhooks: {\n APP_UNINSTALLED: {\n deliveryMethod: DeliveryMethod.Http,\n callbackUrl: \"/webhooks\",\n },\n },\n hooks: {\n afterAuth: async ({ session }) => {\n shopify.registerWebhooks({ session });\n }\n },\n // ...etc\n});\nexport default shopify;\nexport const authenticate = shopify.authenticate;\n\n// /app/routes/webhooks.jsx\nimport { ActionFunctionArgs } from \"@remix-run/node\";\n\nimport { authenticate } from \"../shopify.server\";\nimport db from \"../db.server\";\n\nexport const action = async ({ request }: ActionFunctionArgs) => {\n const { topic, shop } = await authenticate.webhook(request);\n\n switch (topic) {\n case \"APP_UNINSTALLED\":\n await db.session.deleteMany({ where: { shop } });\n break;\n case \"CUSTOMERS_DATA_REQUEST\":\n case \"CUSTOMERS_REDACT\":\n case \"SHOP_REDACT\":\n default:\n throw new Response(\"Unhandled webhook topic\", { status: 404 });\n }\n throw new Response();\n};", "title": "/app/shopify.server.ts" } ] @@ -17170,145 +17532,231 @@ ] }, { - "filePath": "src/server/types.ts", + "filePath": "src/server/config-types.ts", "syntaxKind": "PropertySignature", - "name": "authenticate", - "value": "Authenticate", - "description": "Ways to authenticate requests from different surfaces across Shopify.", + "name": "hooks", + "value": "HooksConfig", + "description": "Functions to call at key places during your apps lifecycle.\n\nThese functions are called in the context of the request that triggered them. This means you can access the session.", + "isOptional": true, "examples": [ { - "title": "Authenticate Shopify requests", - "description": "Use the functions in `authenticate` to validate requests coming from Shopify.", + "title": "Seeding your database custom data when a merchant installs your app", + "description": "", "tabs": [ { - "code": "import { LATEST_API_VERSION, shopifyApp } from \"@shopify/shopify-app-remix/server\";\nimport { restResources } from \"@shopify/shopify-api/rest/admin/2023-04\";\n\nconst shopify = shopifyApp({\n restResources,\n // ...etc\n});\nexport default shopify;", - "title": "/app/shopify.server.ts" - }, - { - "code": "import { LoaderFunctionArgs, json } from \"@remix-run/node\";\nimport shopify from \"../../shopify.server\";\n\nexport async function loader({ request }: LoaderFunctionArgs) {\n const {admin, session, sessionToken, billing} = shopify.authenticate.admin(request);\n\n return json(await admin.rest.resources.Product.count({ session }));\n}", - "title": "/app/routes/**\\/*.jsx" + "code": "import { DeliveryMethod, shopifyApp } from \"@shopify/shopify-app-remix/server\";\nimport { seedStoreData } from \"~/db/seeds\"\n\nconst shopify = shopifyApp({\n hooks: {\n afterAuth: async ({ session }) => {\n seedStoreData({session})\n }\n },\n // ...etc\n});", + "title": "Example" } ] } ] }, { - "filePath": "src/server/types.ts", + "filePath": "src/server/config-types.ts", "syntaxKind": "PropertySignature", - "name": "unauthenticated", - "value": "Unauthenticated>", - "description": "Ways to get Contexts from requests that do not originate from Shopify.", + "name": "isEmbeddedApp", + "value": "boolean", + "description": "Does your app render embedded inside the Shopify Admin or on its own.\n\nUnless you have very specific needs, this should be true.", + "isOptional": true, + "defaultValue": "`true`" + }, + { + "filePath": "src/server/config-types.ts", + "syntaxKind": "PropertySignature", + "name": "distribution", + "value": "AppDistribution", + "description": "How your app is distributed. Default is `AppDistribution.AppStore`.\n\n\n\n\n", + "isOptional": true + }, + { + "filePath": "src/server/config-types.ts", + "syntaxKind": "PropertySignature", + "name": "apiVersion", + "value": "ApiVersion", + "description": "What version of Shopify's Admin API's would you like to use.\n\n\n\n\n", + "isOptional": true, + "defaultValue": "`LATEST_API_VERSION` from `@shopify/shopify-app-remix`", "examples": [ { - "title": "Using unauthenticated contexts", - "description": "Create contexts for requests that don't come from Shopify.", + "title": "Using the latest API Version (Recommended)", + "description": "", "tabs": [ { - "code": "import { LATEST_API_VERSION, shopifyApp } from \"@shopify/shopify-app-remix/server\";\nimport { restResources } from \"@shopify/shopify-api/rest/admin/2023-04\";\n\nconst shopify = shopifyApp({\n restResources,\n // ...etc\n});\nexport default shopify;", - "title": "/app/shopify.server.ts" - }, - { - "code": "import { LoaderFunctionArgs, json } from \"@remix-run/node\";\nimport { authenticateExternal } from \"~/helpers/authenticate\"\nimport shopify from \"../../shopify.server\";\n\nexport async function loader({ request }: LoaderFunctionArgs) {\n const shop = await authenticateExternal(request)\n const {admin} = await shopify.unauthenticated.admin(shop);\n\n return json(await admin.rest.resources.Product.count({ session }));\n}", - "title": "/app/routes/**\\/*.jsx" + "code": "import { LATEST_API_VERSION, shopifyApp } from \"@shopify/shopify-app-remix/server\";\n\nconst shopify = shopifyApp({\n // ...etc\n apiVersion: LATEST_API_VERSION,\n});", + "title": "Example" } ] } ] - } - ], - "value": "export interface ShopifyAppBase {\n /**\n * The `SessionStorage` instance you passed in as a config option.\n *\n * @example\n * Storing sessions with Prisma.\n * Import the `@shopify/shopify-app-session-storage-prisma` package to store sessions in your Prisma database.\n * ```ts\n * // /app/shopify.server.ts\n * import { shopifyApp } from \"@shopify/shopify-app-remix/server\";\n * import { PrismaSessionStorage } from \"@shopify/shopify-app-session-storage-prisma\";\n * import prisma from \"~/db.server\";\n *\n * const shopify = shopifyApp({\n * sessionStorage: new PrismaSessionStorage(prisma),\n * // ...etc\n * })\n *\n * // shopify.sessionStorage is an instance of PrismaSessionStorage\n * ```\n */\n sessionStorage: SessionStorageType;\n\n /**\n * Adds the required Content Security Policy headers for Shopify apps to the given Headers object.\n *\n * {@link https://shopify.dev/docs/apps/store/security/iframe-protection}\n *\n * @example\n * Return headers on all requests.\n * Add headers to all HTML requests by calling `shopify.addDocumentResponseHeaders` in `entry.server.tsx`.\n *\n * ```\n * // ~/shopify.server.ts\n * import { shopifyApp } from \"@shopify/shopify-app-remix/server\";\n *\n * const shopify = shopifyApp({\n * // ...etc\n * });\n * export default shopify;\n * export const addDocumentResponseheaders = shopify.addDocumentResponseheaders;\n * ```\n *\n * ```ts\n * // entry.server.tsx\n * import { addDocumentResponseHeaders } from \"~/shopify.server\";\n *\n * export default function handleRequest(\n * request: Request,\n * responseStatusCode: number,\n * responseHeaders: Headers,\n * remixContext: EntryContext\n * ) {\n * const markup = renderToString(\n * \n * );\n *\n * responseHeaders.set(\"Content-Type\", \"text/html\");\n * addDocumentResponseHeaders(request, responseHeaders);\n *\n * return new Response(\"\" + markup, {\n * status: responseStatusCode,\n * headers: responseHeaders,\n * });\n * }\n * ```\n */\n addDocumentResponseHeaders: AddDocumentResponseHeaders;\n\n /**\n * Register webhook topics for a store using the given session. Most likely you want to use this in combination with the afterAuth hook.\n *\n * @example\n * Registering webhooks after install\n * Trigger the registration to create the webhook subscriptions after a merchant installs your app using the `afterAuth` hook. Learn more about [subscribing to webhooks.](/docs/api/shopify-app-remix/v1/guide-webhooks)\n * ```ts\n * // /app/shopify.server.ts\n * import { DeliveryMethod, shopifyApp } from \"@shopify/shopify-app-remix/server\";\n *\n * const shopify = shopifyApp({\n * hooks: {\n * afterAuth: async ({ session }) => {\n * shopify.registerWebhooks({ session });\n * }\n * },\n * webhooks: {\n * APP_UNINSTALLED: {\n * deliveryMethod: DeliveryMethod.Http,\n * callbackUrl: \"/webhooks\",\n * },\n * },\n * // ...etc\n * });\n * ```\n */\n registerWebhooks: RegisterWebhooks;\n\n /**\n * Ways to authenticate requests from different surfaces across Shopify.\n *\n * @example\n * Authenticate Shopify requests.\n * Use the functions in `authenticate` to validate requests coming from Shopify.\n * ```ts\n * // /app/shopify.server.ts\n * import { LATEST_API_VERSION, shopifyApp } from \"@shopify/shopify-app-remix/server\";\n * import { restResources } from \"@shopify/shopify-api/rest/admin/2023-04\";\n *\n * const shopify = shopifyApp({\n * restResources,\n * // ...etc\n * });\n * export default shopify;\n * ```\n * ```ts\n * // /app/routes/**\\/*.jsx\n * import { LoaderFunctionArgs, json } from \"@remix-run/node\";\n * import shopify from \"../../shopify.server\";\n *\n * export async function loader({ request }: LoaderFunctionArgs) {\n * const {admin, session, sessionToken, billing} = shopify.authenticate.admin(request);\n *\n * return json(await admin.rest.resources.Product.count({ session }));\n * }\n * ```\n */\n authenticate: Authenticate;\n\n /**\n * Ways to get Contexts from requests that do not originate from Shopify.\n *\n * @example\n * Using unauthenticated contexts.\n * Create contexts for requests that don't come from Shopify.\n * ```ts\n * // /app/shopify.server.ts\n * import { LATEST_API_VERSION, shopifyApp } from \"@shopify/shopify-app-remix/server\";\n * import { restResources } from \"@shopify/shopify-api/rest/admin/2023-04\";\n *\n * const shopify = shopifyApp({\n * restResources,\n * // ...etc\n * });\n * export default shopify;\n * ```\n * ```ts\n * // /app/routes/**\\/*.jsx\n * import { LoaderFunctionArgs, json } from \"@remix-run/node\";\n * import { authenticateExternal } from \"~/helpers/authenticate\"\n * import shopify from \"../../shopify.server\";\n *\n * export async function loader({ request }: LoaderFunctionArgs) {\n * const shop = await authenticateExternal(request)\n * const {admin} = await shopify.unauthenticated.admin(shop);\n *\n * return json(await admin.rest.resources.Product.count({ session }));\n * }\n * ```\n */\n unauthenticated: Unauthenticated>;\n}" - }, - "ShopifyAppLogin": { - "filePath": "src/server/types.ts", - "name": "ShopifyAppLogin", - "description": "", - "members": [ + }, { - "filePath": "src/server/types.ts", + "filePath": "src/server/config-types.ts", "syntaxKind": "PropertySignature", - "name": "login", - "value": "Login", - "description": "Log a merchant in, and redirect them to the app root. Will redirect the merchant to authentication if a shop is present in the URL search parameters or form data.\n\nThis function won't be present when the `distribution` config option is set to `AppDistribution.ShopifyAdmin`, because Admin apps aren't allowed to show a login page.", + "name": "authPathPrefix", + "value": "string", + "description": "A path that Shopify can reserve for auth related endpoints.\n\nThis must match a $ route in your Remix app. That route must export a loader function that calls `shopify.authenticate.admin(request)`.", + "isOptional": true, + "defaultValue": "`\"/auth\"`", "examples": [ { - "title": "Creating a login page", - "description": "Use `shopify.login` to create a login form, in a route that can handle GET and POST requests.", + "title": "Using the latest API Version (Recommended)", + "description": "", "tabs": [ { - "code": "import { LATEST_API_VERSION, shopifyApp } from \"@shopify/shopify-app-remix/server\";\n\nconst shopify = shopifyApp({\n // ...etc\n});\nexport default shopify;", + "code": "import { LATEST_API_VERSION, shopifyApp } from \"@shopify/shopify-app-remix/server\";\n\nconst shopify = shopifyApp({\n // ...etc\n apiVersion: LATEST_API_VERSION,\n});\nexport default shopify;\nexport const authenticate = shopify.authenticate;\n\n// /app/routes/auth/$.jsx\nimport { LoaderFunctionArgs } from \"@remix-run/node\";\nimport { authenticate } from \"../../shopify.server\";\n\nexport async function loader({ request }: LoaderFunctionArgs) {\n await authenticate.admin(request);\n\n return null\n}", "title": "/app/shopify.server.ts" - }, - { - "code": "import shopify from \"../../shopify.server\";\n\nexport async function loader({ request }: LoaderFunctionArgs) {\n const errors = shopify.login(request);\n\n return json(errors);\n}\n\nexport async function action({ request }: ActionFunctionArgs) {\n const errors = shopify.login(request);\n\n return json(errors);\n}\n\nexport default function Auth() {\n const actionData = useActionData();\n const [shop, setShop] = useState(\"\");\n\n return (\n \n \n
\n \n \n Login\n \n \n \n \n
\n
\n
\n );\n}", - "title": "/app/routes/auth/login.tsx" } ] } ] + }, + { + "filePath": "src/server/config-types.ts", + "syntaxKind": "PropertySignature", + "name": "future", + "value": "Future", + "description": "Features that will be introduced in future releases of this package.\n\nYou can opt in to these features by setting the corresponding flags. By doing so, you can prepare for future releases in advance and provide feedback on the new features.", + "isOptional": true + }, + { + "filePath": "src/server/config-types.ts", + "syntaxKind": "PropertySignature", + "name": "apiKey", + "value": "string", + "description": "The API key for your app.\n\nAlso known as Client ID in your Partner Dashboard.", + "isOptional": true + }, + { + "filePath": "src/server/config-types.ts", + "syntaxKind": "PropertySignature", + "name": "apiSecretKey", + "value": "string", + "description": "The API secret key for your app.\n\nAlso known as Client Secret in your Partner Dashboard." + }, + { + "filePath": "src/server/config-types.ts", + "syntaxKind": "PropertySignature", + "name": "scopes", + "value": "string[] | AuthScopes", + "description": "The scopes your app needs to access the API.", + "isOptional": true + }, + { + "filePath": "src/server/config-types.ts", + "syntaxKind": "PropertySignature", + "name": "adminApiAccessToken", + "value": "string", + "description": "An app-wide API access token.\n\nOnly applies to custom apps.", + "isOptional": true + }, + { + "filePath": "src/server/config-types.ts", + "syntaxKind": "PropertySignature", + "name": "userAgentPrefix", + "value": "string", + "description": "The user agent prefix to use for API requests.", + "isOptional": true + }, + { + "filePath": "src/server/config-types.ts", + "syntaxKind": "PropertySignature", + "name": "privateAppStorefrontAccessToken", + "value": "string", + "description": "An app-wide API access token for the storefront API.\n\nOnly applies to custom apps.", + "isOptional": true + }, + { + "filePath": "src/server/config-types.ts", + "syntaxKind": "PropertySignature", + "name": "customShopDomains", + "value": "(string | RegExp)[]", + "description": "Override values for Shopify shop domains.", + "isOptional": true + }, + { + "filePath": "src/server/config-types.ts", + "syntaxKind": "PropertySignature", + "name": "billing", + "value": "BillingConfig", + "description": "Billing configurations for the app.", + "isOptional": true + }, + { + "filePath": "src/server/config-types.ts", + "syntaxKind": "PropertySignature", + "name": "restResources", + "value": "Resources", + "description": "REST resources to access the Admin API.\n\nYou can import these from `@shopify/shopify-api/rest/admin/*`.", + "isOptional": true + }, + { + "filePath": "src/server/config-types.ts", + "syntaxKind": "PropertySignature", + "name": "logger", + "value": "{ log?: LogFunction; level?: LogSeverity; httpRequests?: boolean; timestamps?: boolean; }", + "description": "Customization options for Shopify logs.", + "isOptional": true } ], - "value": "interface ShopifyAppLogin {\n /**\n * Log a merchant in, and redirect them to the app root. Will redirect the merchant to authentication if a shop is\n * present in the URL search parameters or form data.\n *\n * This function won't be present when the `distribution` config option is set to `AppDistribution.ShopifyAdmin`,\n * because Admin apps aren't allowed to show a login page.\n *\n * @example\n * Creating a login page.\n * Use `shopify.login` to create a login form, in a route that can handle GET and POST requests.\n * ```ts\n * // /app/shopify.server.ts\n * import { LATEST_API_VERSION, shopifyApp } from \"@shopify/shopify-app-remix/server\";\n *\n * const shopify = shopifyApp({\n * // ...etc\n * });\n * export default shopify;\n * ```\n * ```ts\n * // /app/routes/auth/login.tsx\n * import shopify from \"../../shopify.server\";\n *\n * export async function loader({ request }: LoaderFunctionArgs) {\n * const errors = shopify.login(request);\n *\n * return json(errors);\n * }\n *\n * export async function action({ request }: ActionFunctionArgs) {\n * const errors = shopify.login(request);\n *\n * return json(errors);\n * }\n *\n * export default function Auth() {\n * const actionData = useActionData();\n * const [shop, setShop] = useState(\"\");\n *\n * return (\n * \n * \n *
\n * \n * \n * Login\n * \n * \n * \n * \n *
\n *
\n *
\n * );\n * }\n * ```\n */\n login: Login;\n}" + "value": "export interface AppConfigArg<\n Resources extends ShopifyRestResources = ShopifyRestResources,\n Storage extends SessionStorage = SessionStorage,\n Future extends FutureFlagOptions = FutureFlagOptions,\n> extends Omit<\n ApiConfigArg>,\n | 'hostName'\n | 'hostScheme'\n | 'isEmbeddedApp'\n | 'apiVersion'\n | 'isCustomStoreApp'\n | 'future'\n > {\n /**\n * The URL your app is running on.\n *\n * 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.\n */\n appUrl: string;\n\n /**\n * An adaptor for storing sessions in your database of choice.\n *\n * Shopify provides multiple session storage adaptors and you can create your own.\n *\n * {@link https://github.com/Shopify/shopify-app-js/blob/main/README.md#session-storage-options}\n *\n * @example\n * Storing sessions with Prisma.\n * Add the `@shopify/shopify-app-session-storage-prisma` package to use the Prisma session storage.\n * ```ts\n * import { shopifyApp } from \"@shopify/shopify-app-remix/server\";\n * import { PrismaSessionStorage } from \"@shopify/shopify-app-session-storage-prisma\";\n *\n * import prisma from \"~/db.server\";\n *\n * const shopify = shopifyApp({\n * // ... etc\n * sessionStorage: new PrismaSessionStorage(prisma),\n * });\n * export default shopify;\n * ```\n */\n sessionStorage: Storage;\n\n /**\n * Whether your app use online or offline tokens.\n *\n * 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.\n *\n * {@link https://shopify.dev/docs/apps/auth/oauth/access-modes}\n *\n * @defaultValue `false`\n */\n useOnlineTokens?: boolean;\n\n /**\n * The config for the webhook topics your app would like to subscribe to.\n *\n * {@link https://shopify.dev/docs/apps/webhooks}\n *\n * This can be in used in conjunction with the afterAuth hook to register webhook topics when a user installs your app. Or you can use this function in other processes such as background jobs.\n *\n * @example\n * Registering for a webhook when a merchant uninstalls your app.\n * ```ts\n * // /app/shopify.server.ts\n * import { DeliveryMethod, shopifyApp } from \"@shopify/shopify-app-remix/server\";\n *\n * const shopify = shopifyApp({\n * webhooks: {\n * APP_UNINSTALLED: {\n * deliveryMethod: DeliveryMethod.Http,\n * callbackUrl: \"/webhooks\",\n * },\n * },\n * hooks: {\n * afterAuth: async ({ session }) => {\n * shopify.registerWebhooks({ session });\n * }\n * },\n * // ...etc\n * });\n * export default shopify;\n * export const authenticate = shopify.authenticate;\n *\n * // /app/routes/webhooks.jsx\n * import { ActionFunctionArgs } from \"@remix-run/node\";\n *\n * import { authenticate } from \"../shopify.server\";\n * import db from \"../db.server\";\n *\n * export const action = async ({ request }: ActionFunctionArgs) => {\n * const { topic, shop } = await authenticate.webhook(request);\n *\n * switch (topic) {\n * case \"APP_UNINSTALLED\":\n * await db.session.deleteMany({ where: { shop } });\n * break;\n * case \"CUSTOMERS_DATA_REQUEST\":\n * case \"CUSTOMERS_REDACT\":\n * case \"SHOP_REDACT\":\n * default:\n * throw new Response(\"Unhandled webhook topic\", { status: 404 });\n * }\n * throw new Response();\n * };\n * ```\n */\n webhooks?: WebhookConfig;\n\n /**\n * Functions to call at key places during your apps lifecycle.\n *\n * These functions are called in the context of the request that triggered them. This means you can access the session.\n *\n * @example\n * Seeding your database custom data when a merchant installs your app.\n * ```ts\n * import { DeliveryMethod, shopifyApp } from \"@shopify/shopify-app-remix/server\";\n * import { seedStoreData } from \"~/db/seeds\"\n *\n * const shopify = shopifyApp({\n * hooks: {\n * afterAuth: async ({ session }) => {\n * seedStoreData({session})\n * }\n * },\n * // ...etc\n * });\n * ```\n */\n hooks?: HooksConfig;\n\n /**\n * Does your app render embedded inside the Shopify Admin or on its own.\n *\n * Unless you have very specific needs, this should be true.\n *\n * @defaultValue `true`\n */\n isEmbeddedApp?: boolean;\n\n /**\n * How your app is distributed. Default is `AppDistribution.AppStore`.\n *\n * {@link https://shopify.dev/docs/apps/distribution}\n */\n distribution?: AppDistribution;\n\n /**\n * What version of Shopify's Admin API's would you like to use.\n *\n * {@link https://shopify.dev/docs/api/}\n *\n * @defaultValue `LATEST_API_VERSION` from `@shopify/shopify-app-remix`\n *\n * @example\n * Using the latest API Version (Recommended)\n * ```ts\n * import { LATEST_API_VERSION, shopifyApp } from \"@shopify/shopify-app-remix/server\";\n *\n * const shopify = shopifyApp({\n * // ...etc\n * apiVersion: LATEST_API_VERSION,\n * });\n * ```\n */\n apiVersion?: ApiVersion;\n\n /**\n * A path that Shopify can reserve for auth related endpoints.\n *\n * This must match a $ route in your Remix app. That route must export a loader function that calls `shopify.authenticate.admin(request)`.\n *\n * @default `\"/auth\"`\n *\n * @example\n * Using the latest API Version (Recommended)\n * ```ts\n * // /app/shopify.server.ts\n * import { LATEST_API_VERSION, shopifyApp } from \"@shopify/shopify-app-remix/server\";\n *\n * const shopify = shopifyApp({\n * // ...etc\n * apiVersion: LATEST_API_VERSION,\n * });\n * export default shopify;\n * export const authenticate = shopify.authenticate;\n *\n * // /app/routes/auth/$.jsx\n * import { LoaderFunctionArgs } from \"@remix-run/node\";\n * import { authenticate } from \"../../shopify.server\";\n *\n * export async function loader({ request }: LoaderFunctionArgs) {\n * await authenticate.admin(request);\n *\n * return null\n * }\n * ```\n */\n authPathPrefix?: string;\n\n /**\n * Features that will be introduced in future releases of this package.\n *\n * You can opt in to these features by setting the corresponding flags. By doing so, you can prepare for future\n * releases in advance and provide feedback on the new features.\n */\n future?: Future;\n}" }, - "Login": { - "filePath": "src/server/types.ts", - "name": "Login", + "WebhookConfig": { + "filePath": "src/server/config-types.ts", + "syntaxKind": "TypeAliasDeclaration", + "name": "WebhookConfig", + "value": "Record", "description": "", - "params": [ - { - "name": "request", - "description": "", - "value": "Request", - "filePath": "src/server/types.ts" - } - ], - "returns": { - "filePath": "src/server/types.ts", - "description": "", - "name": "Promise", - "value": "Promise" - }, - "value": "type Login = (request: Request) => Promise;" + "members": [] }, - "LoginError": { - "filePath": "src/server/types.ts", - "name": "LoginError", + "HooksConfig": { + "filePath": "src/server/config-types.ts", + "name": "HooksConfig", "description": "", "members": [ { - "filePath": "src/server/types.ts", + "filePath": "src/server/config-types.ts", "syntaxKind": "PropertySignature", - "name": "shop", - "value": "LoginErrorType", - "description": "", - "isOptional": true + "name": "afterAuth", + "value": "(options: AfterAuthOptions) => void | Promise", + "description": "A function to call after a merchant installs your app", + "isOptional": true, + "examples": [ + { + "title": "Registering webhooks and seeding data when a merchant installs your app", + "description": "", + "tabs": [ + { + "code": "import { DeliveryMethod, shopifyApp } from \"@shopify/shopify-app-remix/server\";\nimport { seedStoreData } from \"~/db/seeds\"\n\nconst shopify = shopifyApp({\n hooks: {\n afterAuth: async ({ session }) => {\n shopify.registerWebhooks({ session });\n seedStoreData({session})\n }\n },\n webhooks: {\n APP_UNINSTALLED: {\n deliveryMethod: DeliveryMethod.Http,\n callbackUrl: \"/webhooks\",\n },\n },\n // ...etc\n});", + "title": "Example" + } + ] + } + ] } ], - "value": "export interface LoginError {\n shop?: LoginErrorType;\n}" + "value": "interface HooksConfig {\n /**\n * A function to call after a merchant installs your app\n *\n * @param context - An object with context about the request that triggered the hook.\n * @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.\n * @param context.admin - An object with access to the Shopify Admin API's.\n *\n * @example\n * Registering webhooks and seeding data when a merchant installs your app.\n * ```ts\n * import { DeliveryMethod, shopifyApp } from \"@shopify/shopify-app-remix/server\";\n * import { seedStoreData } from \"~/db/seeds\"\n *\n * const shopify = shopifyApp({\n * hooks: {\n * afterAuth: async ({ session }) => {\n * shopify.registerWebhooks({ session });\n * seedStoreData({session})\n * }\n * },\n * webhooks: {\n * APP_UNINSTALLED: {\n * deliveryMethod: DeliveryMethod.Http,\n * callbackUrl: \"/webhooks\",\n * },\n * },\n * // ...etc\n * });\n * ```\n */\n afterAuth?: (options: AfterAuthOptions) => void | Promise;\n}" }, - "LoginErrorType": { - "filePath": "src/server/types.ts", - "syntaxKind": "EnumDeclaration", - "name": "LoginErrorType", - "value": "export enum LoginErrorType {\n MissingShop = 'MISSING_SHOP',\n InvalidShop = 'INVALID_SHOP',\n}", + "AfterAuthOptions": { + "filePath": "src/server/config-types.ts", + "name": "AfterAuthOptions", + "description": "", "members": [ { - "filePath": "src/server/types.ts", - "name": "MissingShop", - "value": "MISSING_SHOP" + "filePath": "src/server/config-types.ts", + "syntaxKind": "PropertySignature", + "name": "session", + "value": "Session", + "description": "" }, { - "filePath": "src/server/types.ts", - "name": "InvalidShop", - "value": "INVALID_SHOP" + "filePath": "src/server/config-types.ts", + "syntaxKind": "PropertySignature", + "name": "admin", + "value": "AdminApiContext", + "description": "" } - ] - }, - "AppStoreApp": { - "filePath": "src/server/types.ts", - "syntaxKind": "TypeAliasDeclaration", - "name": "AppStoreApp", - "value": "ShopifyAppBase & ShopifyAppLogin", - "description": "" + ], + "value": "export interface AfterAuthOptions<\n R extends ShopifyRestResources = ShopifyRestResources,\n> {\n session: Session;\n admin: AdminApiContext;\n}" } } }, @@ -17879,7 +18327,7 @@ "description": "Getting the number of orders in a store using REST resources. Visit the [Admin REST API references](/docs/api/admin-rest) for examples on using each resource.", "tabs": [ { - "code": "import { LoaderFunctionArgs, json } from \"@remix-run/node\";\nimport { authenticate } from \"../shopify.server\";\n\nexport const loader = async ({ request }: LoaderFunctionArgs) => {\n const { admin, session } = await authenticate.admin(request);\n return json(admin.rest.resources.Order.count({ session }));\n};", + "code": "import { LoaderFunctionArgs, json } from \"@remix-run/node\";\nimport { authenticate } from \"../shopify.server\";\n\nexport const loader = async ({ request }: LoaderFunctionArgs) => {\n const {\n admin,\n session,\n } = await authenticate.admin(request);\n\n return json(\n admin.rest.resources.Order.count({ session }),\n );\n};", "title": "/app/routes/**\\/*.ts" }, { @@ -17893,7 +18341,7 @@ "description": "Use `admin.rest.get` to make custom requests to make a request to to the `customer/count` endpoint", "tabs": [ { - "code": "import { LoaderFunctionArgs, json } from \"@remix-run/node\";\nimport { authenticate } from \"../shopify.server\";\n\nexport const loader = async ({ request }: LoaderFunctionArgs) => {\n const { admin, session } = await authenticate.admin(request);\n const response = await admin.rest.get({ path: \"/customers/count.json\" });\n const customers = await response.json();\n return json({ customers });\n};", + "code": "import { LoaderFunctionArgs, json } from \"@remix-run/node\";\nimport { authenticate } from \"../shopify.server\";\n\nexport const loader = async ({ request }: LoaderFunctionArgs) => {\n const {\n admin,\n session,\n } = await authenticate.admin(request);\n\n const response = await admin.rest.get({\n path: \"/customers/count.json\",\n });\n const customers = await response.json();\n\n return json({ customers });\n};", "title": "/app/routes/**\\/*.ts" }, { @@ -17907,7 +18355,7 @@ "description": "Use `admin.rest.post` to make custom requests to make a request to to the `customers.json` endpoint to send a welcome email", "tabs": [ { - "code": "import { LoaderFunctionArgs, json } from \"@remix-run/node\";\nimport { authenticate } from \"../shopify.server\";\n\nexport const loader = async ({ request }: LoaderFunctionArgs) => {\n const { admin, session } = await authenticate.admin(request);\n const response = admin.rest.post({\n path: \"customers/7392136888625/send_invite.json\",\n body: {\n customer_invite: {\n to: \"new_test_email@shopify.com\",\n from: \"j.limited@example.com\",\n bcc: [\"j.limited@example.com\"],\n subject: \"Welcome to my new shop\",\n custom_message: \"My awesome new store\",\n },\n },\n});\n const customerInvite = await response.json();\n return json({ customerInvite });\n};", + "code": "import { LoaderFunctionArgs, json } from \"@remix-run/node\";\nimport { authenticate } from \"../shopify.server\";\n\nexport const loader = async ({ request }: LoaderFunctionArgs) => {\n const {\n admin,\n session,\n } = await authenticate.admin(request);\n\n const response = admin.rest.post({\n path: \"customers/7392136888625/send_invite.json\",\n body: {\n customer_invite: {\n to: \"new_test_email@shopify.com\",\n from: \"j.limited@example.com\",\n bcc: [\"j.limited@example.com\"],\n subject: \"Welcome to my new shop\",\n custom_message: \"My awesome new store\",\n },\n },\n });\n\n const customerInvite = await response.json();\n return json({ customerInvite });\n};", "title": "/app/routes/**\\/*.ts" }, { @@ -17930,11 +18378,25 @@ "description": "Use `admin.graphql` to make query / mutation requests.", "tabs": [ { - "code": "import { ActionFunctionArgs } from \"@remix-run/node\";\nimport { authenticate } from \"../shopify.server\";\n\nexport async function action({ request }: ActionFunctionArgs) {\n const { admin } = await authenticate.admin(request);\n\n const response = await admin.graphql(\n `#graphql\n mutation populateProduct($input: ProductInput!) {\n productCreate(input: $input) {\n product {\n id\n }\n }\n }`,\n { variables: { input: { title: \"Product Name\" } } }\n );\n\n const productData = await response.json();\n return json({ data: productData.data });\n}", - "title": "Example" + "code": "import { ActionFunctionArgs } from \"@remix-run/node\";\nimport { authenticate } from \"../shopify.server\";\n\nexport const action = async ({ request }: ActionFunctionArgs) => {\n const { admin } = await authenticate.admin(request);\n\n const response = await admin.graphql(\n `#graphql\n mutation populateProduct($input: ProductInput!) {\n productCreate(input: $input) {\n product {\n id\n }\n }\n }`,\n {\n variables: {\n input: { title: \"Product Name\" },\n },\n },\n );\n\n const productData = await response.json();\n return json({\n productId: productData.data?.productCreate?.product?.id,\n });\n}", + "title": "/app/routes/**\\/*.ts" + }, + { + "code": "import { shopifyApp } from \"@shopify/shopify-app-remix/server\";\n\nconst shopify = shopifyApp({\n // ...\n});\nexport default shopify;\nexport const authenticate = shopify.authenticate;", + "title": "/app/shopify.server.ts" + } + ] + }, + { + "title": "Handling GraphQL errors", + "description": "Catch `GraphqlQueryError` errors to see error messages from the API.", + "tabs": [ + { + "code": "import { ActionFunctionArgs } from \"@remix-run/node\";\nimport { authenticate } from \"../shopify.server\";\n\nexport const action = async ({ request }: ActionFunctionArgs) => {\n const { admin } = await authenticate.admin(request);\n\n try {\n const response = await admin.graphql(\n `#graphql\n query incorrectQuery {\n products(first: 10) {\n nodes {\n not_a_field\n }\n }\n }`,\n );\n\n return json({ data: await response.json() });\n } catch (error) {\n if (error instanceof GraphqlQueryError) {\n // error.body.errors:\n // { graphQLErrors: [\n // { message: \"Field 'not_a_field' doesn't exist on type 'Product'\" }\n // ] }\n return json({ errors: error.body?.errors }, { status: 500 });\n }\n return json({ message: \"An error occurred\" }, { status: 500 });\n }\n}", + "title": "/app/routes/**\\/*.ts" }, { - "code": "import { shopifyApp } from \"@shopify/shopify-app-remix/server\";\n\nconst shopify = shopifyApp({\n restResources,\n // ...etc\n});\nexport default shopify;\nexport const unauthenticated = shopify.unauthenticated;", + "code": "import { shopifyApp } from \"@shopify/shopify-app-remix/server\";\n\nconst shopify = shopifyApp({\n // ...\n});\nexport default shopify;\nexport const authenticate = shopify.authenticate;", "title": "/app/shopify.server.ts" } ] @@ -17942,7 +18404,7 @@ ] } ], - "value": "export interface AdminApiContext<\n Resources extends ShopifyRestResources = ShopifyRestResources,\n> {\n /**\n * Methods for interacting with the Shopify Admin REST API\n *\n * There are methods for interacting with individual REST resources. You can also make `GET`, `POST`, `PUT` and `DELETE` requests should the REST resources not meet your needs.\n *\n * {@link https://shopify.dev/docs/api/admin-rest}\n *\n * @example\n * Using REST resources.\n * Getting the number of orders in a store using REST resources. Visit the [Admin REST API references](/docs/api/admin-rest) for examples on using each resource. \n *\n * ```ts\n * // /app/routes/**\\/*.ts\n * import { LoaderFunctionArgs, json } from \"@remix-run/node\";\n * import { authenticate } from \"../shopify.server\";\n *\n * export const loader = async ({ request }: LoaderFunctionArgs) => {\n * const { admin, session } = await authenticate.admin(request);\n * return json(admin.rest.resources.Order.count({ session }));\n * };\n * ```\n *\n *\n * ```ts\n * // /app/shopify.server.ts\n * import { shopifyApp } from \"@shopify/shopify-app-remix/server\";\n * import { restResources } from \"@shopify/shopify-api/rest/admin/2023-07\";\n *\n * const shopify = shopifyApp({\n * restResources,\n * // ...etc\n * });\n * export default shopify;\n * export const authenticate = shopify.authenticate;\n * ```\n *\n *\n * @example\n * Performing a GET request to the REST API.\n * Use `admin.rest.get` to make custom requests to make a request to to the `customer/count` endpoint\n *\n * ```ts\n * // /app/routes/**\\/*.ts\n * import { LoaderFunctionArgs, json } from \"@remix-run/node\";\n * import { authenticate } from \"../shopify.server\";\n *\n * export const loader = async ({ request }: LoaderFunctionArgs) => {\n * const { admin, session } = await authenticate.admin(request);\n * const response = await admin.rest.get({ path: \"/customers/count.json\" });\n * const customers = await response.json();\n * return json({ customers });\n * };\n * ```\n *\n * ```ts\n * // /app/shopify.server.ts\n * import { shopifyApp } from \"@shopify/shopify-app-remix/server\";\n * import { restResources } from \"@shopify/shopify-api/rest/admin/2023-04\";\n *\n * const shopify = shopifyApp({\n * restResources,\n * // ...etc\n * });\n * export default shopify;\n * export const authenticate = shopify.authenticate;\n * ```\n * @example\n * Performing a POST request to the REST API.\n * Use `admin.rest.post` to make custom requests to make a request to to the `customers.json` endpoint to send a welcome email\n * ```ts\n * // /app/routes/**\\/*.ts\n * import { LoaderFunctionArgs, json } from \"@remix-run/node\";\n * import { authenticate } from \"../shopify.server\";\n *\n * export const loader = async ({ request }: LoaderFunctionArgs) => {\n * const { admin, session } = await authenticate.admin(request);\n * const response = admin.rest.post({\n * path: \"customers/7392136888625/send_invite.json\",\n * body: {\n * customer_invite: {\n * to: \"new_test_email@shopify.com\",\n * from: \"j.limited@example.com\",\n * bcc: [\"j.limited@example.com\"],\n * subject: \"Welcome to my new shop\",\n * custom_message: \"My awesome new store\",\n * },\n * },\n * });\n * const customerInvite = await response.json();\n * return json({ customerInvite });\n * };\n * ```\n *\n * ```ts\n * // /app/shopify.server.ts\n * import { shopifyApp } from \"@shopify/shopify-app-remix/server\";\n * import { restResources } from \"@shopify/shopify-api/rest/admin/2023-04\";\n *\n * const shopify = shopifyApp({\n * restResources,\n * // ...etc\n * });\n * export default shopify;\n * export const authenticate = shopify.authenticate;\n * ```\n */\n rest: RestClientWithResources;\n\n /**\n * Methods for interacting with the Shopify Admin GraphQL API\n *\n * {@link https://shopify.dev/docs/api/admin-graphql}\n * {@link https://github.com/Shopify/shopify-api-js/blob/main/packages/shopify-api/docs/reference/clients/Graphql.md}\n *\n * @example\n * Querying the GraphQL API.\n * Use `admin.graphql` to make query / mutation requests.\n * ```ts\n * import { ActionFunctionArgs } from \"@remix-run/node\";\n * import { authenticate } from \"../shopify.server\";\n *\n * export async function action({ request }: ActionFunctionArgs) {\n * const { admin } = await authenticate.admin(request);\n *\n * const response = await admin.graphql(\n * `#graphql\n * mutation populateProduct($input: ProductInput!) {\n * productCreate(input: $input) {\n * product {\n * id\n * }\n * }\n * }`,\n * { variables: { input: { title: \"Product Name\" } } }\n * );\n *\n * const productData = await response.json();\n * return json({ data: productData.data });\n * }\n * ```\n *\n * ```ts\n * // /app/shopify.server.ts\n * import { shopifyApp } from \"@shopify/shopify-app-remix/server\";\n *\n * const shopify = shopifyApp({\n * restResources,\n * // ...etc\n * });\n * export default shopify;\n * export const unauthenticated = shopify.unauthenticated;\n * ```\n */\n graphql: GraphQLClient;\n}" + "value": "export interface AdminApiContext<\n Resources extends ShopifyRestResources = ShopifyRestResources,\n> {\n /**\n * Methods for interacting with the Shopify Admin REST API\n *\n * There are methods for interacting with individual REST resources. You can also make `GET`, `POST`, `PUT` and `DELETE` requests should the REST resources not meet your needs.\n *\n * {@link https://shopify.dev/docs/api/admin-rest}\n *\n * @example\n * Using REST resources.\n * Getting the number of orders in a store using REST resources. Visit the [Admin REST API references](/docs/api/admin-rest) for examples on using each resource. \n *\n * ```ts\n * // /app/routes/**\\/*.ts\n * import { LoaderFunctionArgs, json } from \"@remix-run/node\";\n * import { authenticate } from \"../shopify.server\";\n *\n * export const loader = async ({ request }: LoaderFunctionArgs) => {\n * const {\n * admin,\n * session,\n * } = await authenticate.admin(request);\n *\n * return json(\n * admin.rest.resources.Order.count({ session }),\n * );\n * };\n * ```\n *\n * ```ts\n * // /app/shopify.server.ts\n * import { shopifyApp } from \"@shopify/shopify-app-remix/server\";\n * import { restResources } from \"@shopify/shopify-api/rest/admin/2023-07\";\n *\n * const shopify = shopifyApp({\n * restResources,\n * // ...etc\n * });\n * export default shopify;\n * export const authenticate = shopify.authenticate;\n * ```\n *\n * @example\n * Performing a GET request to the REST API.\n * Use `admin.rest.get` to make custom requests to make a request to to the `customer/count` endpoint\n *\n * ```ts\n * // /app/routes/**\\/*.ts\n * import { LoaderFunctionArgs, json } from \"@remix-run/node\";\n * import { authenticate } from \"../shopify.server\";\n *\n * export const loader = async ({ request }: LoaderFunctionArgs) => {\n * const {\n * admin,\n * session,\n * } = await authenticate.admin(request);\n *\n * const response = await admin.rest.get({\n * path: \"/customers/count.json\",\n * });\n * const customers = await response.json();\n *\n * return json({ customers });\n * };\n * ```\n *\n * ```ts\n * // /app/shopify.server.ts\n * import { shopifyApp } from \"@shopify/shopify-app-remix/server\";\n * import { restResources } from \"@shopify/shopify-api/rest/admin/2023-04\";\n *\n * const shopify = shopifyApp({\n * restResources,\n * // ...etc\n * });\n * export default shopify;\n * export const authenticate = shopify.authenticate;\n * ```\n *\n * @example\n * Performing a POST request to the REST API.\n * Use `admin.rest.post` to make custom requests to make a request to to the `customers.json` endpoint to send a welcome email\n * ```ts\n * // /app/routes/**\\/*.ts\n * import { LoaderFunctionArgs, json } from \"@remix-run/node\";\n * import { authenticate } from \"../shopify.server\";\n *\n * export const loader = async ({ request }: LoaderFunctionArgs) => {\n * const {\n * admin,\n * session,\n * } = await authenticate.admin(request);\n *\n * const response = admin.rest.post({\n * path: \"customers/7392136888625/send_invite.json\",\n * body: {\n * customer_invite: {\n * to: \"new_test_email@shopify.com\",\n * from: \"j.limited@example.com\",\n * bcc: [\"j.limited@example.com\"],\n * subject: \"Welcome to my new shop\",\n * custom_message: \"My awesome new store\",\n * },\n * },\n * });\n *\n * const customerInvite = await response.json();\n * return json({ customerInvite });\n * };\n * ```\n *\n * ```ts\n * // /app/shopify.server.ts\n * import { shopifyApp } from \"@shopify/shopify-app-remix/server\";\n * import { restResources } from \"@shopify/shopify-api/rest/admin/2023-04\";\n *\n * const shopify = shopifyApp({\n * restResources,\n * // ...etc\n * });\n * export default shopify;\n * export const authenticate = shopify.authenticate;\n * ```\n */\n rest: RestClientWithResources;\n\n /**\n * Methods for interacting with the Shopify Admin GraphQL API\n *\n * {@link https://shopify.dev/docs/api/admin-graphql}\n * {@link https://github.com/Shopify/shopify-api-js/blob/main/packages/shopify-api/docs/reference/clients/Graphql.md}\n *\n * @example\n * Querying the GraphQL API.\n * Use `admin.graphql` to make query / mutation requests.\n * ```ts\n * // /app/routes/**\\/*.ts\n * import { ActionFunctionArgs } from \"@remix-run/node\";\n * import { authenticate } from \"../shopify.server\";\n *\n * export const action = async ({ request }: ActionFunctionArgs) => {\n * const { admin } = await authenticate.admin(request);\n *\n * const response = await admin.graphql(\n * `#graphql\n * mutation populateProduct($input: ProductInput!) {\n * productCreate(input: $input) {\n * product {\n * id\n * }\n * }\n * }`,\n * {\n * variables: {\n * input: { title: \"Product Name\" },\n * },\n * },\n * );\n *\n * const productData = await response.json();\n * return json({\n * productId: productData.data?.productCreate?.product?.id,\n * });\n * }\n * ```\n *\n * ```ts\n * // /app/shopify.server.ts\n * import { shopifyApp } from \"@shopify/shopify-app-remix/server\";\n *\n * const shopify = shopifyApp({\n * // ...\n * });\n * export default shopify;\n * export const authenticate = shopify.authenticate;\n * ```\n *\n * @example\n * Handling GraphQL errors.\n * Catch `GraphqlQueryError` errors to see error messages from the API.\n * ```ts\n * // /app/routes/**\\/*.ts\n * import { ActionFunctionArgs } from \"@remix-run/node\";\n * import { authenticate } from \"../shopify.server\";\n *\n * export const action = async ({ request }: ActionFunctionArgs) => {\n * const { admin } = await authenticate.admin(request);\n *\n * try {\n * const response = await admin.graphql(\n * `#graphql\n * query incorrectQuery {\n * products(first: 10) {\n * nodes {\n * not_a_field\n * }\n * }\n * }`,\n * );\n *\n * return json({ data: await response.json() });\n * } catch (error) {\n * if (error instanceof GraphqlQueryError) {\n * // error.body.errors:\n * // { graphQLErrors: [\n * // { message: \"Field 'not_a_field' doesn't exist on type 'Product'\" }\n * // ] }\n * return json({ errors: error.body?.errors }, { status: 500 });\n * }\n * return json({ message: \"An error occurred\" }, { status: 500 });\n * }\n * }\n * ```\n *\n * ```ts\n * // /app/shopify.server.ts\n * import { shopifyApp } from \"@shopify/shopify-app-remix/server\";\n *\n * const shopify = shopifyApp({\n * // ...\n * });\n * export default shopify;\n * export const authenticate = shopify.authenticate;\n * ```\n */\n graphql: GraphQLClient;\n}" }, "RestClientWithResources": { "filePath": "src/server/clients/admin/rest.ts", @@ -18095,6 +18557,116 @@ "value": "GetRequestParams & {\n data: Record | string;\n}", "description": "" }, + "GraphQLClient": { + "filePath": "src/server/clients/types.ts", + "name": "GraphQLClient", + "description": "", + "params": [ + { + "name": "query", + "description": "", + "value": "Operation extends keyof Operations", + "filePath": "src/server/clients/types.ts" + }, + { + "name": "options", + "description": "", + "value": "GraphQLQueryOptions", + "isOptional": true, + "filePath": "src/server/clients/types.ts" + } + ], + "returns": { + "filePath": "src/server/clients/types.ts", + "description": "", + "name": "interface Promise {\n /**\n * Attaches callbacks for the resolution and/or rejection of the Promise.\n * @param onfulfilled The callback to execute when the Promise is resolved.\n * @param onrejected The callback to execute when the Promise is rejected.\n * @returns A Promise for the completion of which ever callback is executed.\n */\n then(onfulfilled?: ((value: T) => TResult1 | PromiseLike) | undefined | null, onrejected?: ((reason: any) => TResult2 | PromiseLike) | undefined | null): Promise;\n\n /**\n * Attaches a callback for only the rejection of the Promise.\n * @param onrejected The callback to execute when the Promise is rejected.\n * @returns A Promise for the completion of the callback.\n */\n catch(onrejected?: ((reason: any) => TResult | PromiseLike) | undefined | null): Promise;\n}, interface Promise {}, Promise: PromiseConstructor, interface Promise {\n readonly [Symbol.toStringTag]: string;\n}, interface Promise {\n /**\n * Attaches a callback that is invoked when the Promise is settled (fulfilled or rejected). The\n * resolved value cannot be modified from the callback.\n * @param onfinally The callback to execute when the Promise is settled (fulfilled or rejected).\n * @returns A Promise for the completion of the callback.\n */\n finally(onfinally?: (() => void) | undefined | null): Promise;\n}", + "value": "interface Promise {\n /**\n * Attaches callbacks for the resolution and/or rejection of the Promise.\n * @param onfulfilled The callback to execute when the Promise is resolved.\n * @param onrejected The callback to execute when the Promise is rejected.\n * @returns A Promise for the completion of which ever callback is executed.\n */\n then(onfulfilled?: ((value: T) => TResult1 | PromiseLike) | undefined | null, onrejected?: ((reason: any) => TResult2 | PromiseLike) | undefined | null): Promise;\n\n /**\n * Attaches a callback for only the rejection of the Promise.\n * @param onrejected The callback to execute when the Promise is rejected.\n * @returns A Promise for the completion of the callback.\n */\n catch(onrejected?: ((reason: any) => TResult | PromiseLike) | undefined | null): Promise;\n}, interface Promise {}, Promise: PromiseConstructor, interface Promise {\n readonly [Symbol.toStringTag]: string;\n}, interface Promise {\n /**\n * Attaches a callback that is invoked when the Promise is settled (fulfilled or rejected). The\n * resolved value cannot be modified from the callback.\n * @param onfinally The callback to execute when the Promise is settled (fulfilled or rejected).\n * @returns A Promise for the completion of the callback.\n */\n finally(onfinally?: (() => void) | undefined | null): Promise;\n}" + }, + "value": "export type GraphQLClient = <\n Operation extends keyof Operations,\n>(\n query: Operation,\n options?: GraphQLQueryOptions,\n) => Promise>;" + }, + "GraphQLQueryOptions": { + "filePath": "src/server/clients/types.ts", + "name": "GraphQLQueryOptions", + "description": "", + "members": [ + { + "filePath": "src/server/clients/types.ts", + "syntaxKind": "PropertySignature", + "name": "variables", + "value": "ApiClientRequestOptions[\"variables\"]", + "description": "The variables to pass to the operation.", + "isOptional": true + }, + { + "filePath": "src/server/clients/types.ts", + "syntaxKind": "PropertySignature", + "name": "apiVersion", + "value": "ApiVersion", + "description": "The version of the API to use for the request.", + "isOptional": true + }, + { + "filePath": "src/server/clients/types.ts", + "syntaxKind": "PropertySignature", + "name": "headers", + "value": "Record", + "description": "Additional headers to include in the request.", + "isOptional": true + }, + { + "filePath": "src/server/clients/types.ts", + "syntaxKind": "PropertySignature", + "name": "tries", + "value": "number", + "description": "The total number of times to try the request if it fails.", + "isOptional": true + } + ], + "value": "export interface GraphQLQueryOptions<\n Operation extends keyof Operations,\n Operations extends AllOperations,\n> {\n /**\n * The variables to pass to the operation.\n */\n variables?: ApiClientRequestOptions['variables'];\n /**\n * The version of the API to use for the request.\n */\n apiVersion?: ApiVersion;\n /**\n * Additional headers to include in the request.\n */\n headers?: Record;\n /**\n * The total number of times to try the request if it fails.\n */\n tries?: number;\n}" + }, + "ApiVersion": { + "filePath": "../../node_modules/@shopify/shopify-api/lib/types.d.ts", + "syntaxKind": "EnumDeclaration", + "name": "ApiVersion", + "value": "export declare enum ApiVersion {\n October22 = \"2022-10\",\n January23 = \"2023-01\",\n April23 = \"2023-04\",\n July23 = \"2023-07\",\n October23 = \"2023-10\",\n January24 = \"2024-01\",\n Unstable = \"unstable\"\n}", + "members": [ + { + "filePath": "../../node_modules/@shopify/shopify-api/lib/types.d.ts", + "name": "October22", + "value": "2022-10" + }, + { + "filePath": "../../node_modules/@shopify/shopify-api/lib/types.d.ts", + "name": "January23", + "value": "2023-01" + }, + { + "filePath": "../../node_modules/@shopify/shopify-api/lib/types.d.ts", + "name": "April23", + "value": "2023-04" + }, + { + "filePath": "../../node_modules/@shopify/shopify-api/lib/types.d.ts", + "name": "July23", + "value": "2023-07" + }, + { + "filePath": "../../node_modules/@shopify/shopify-api/lib/types.d.ts", + "name": "October23", + "value": "2023-10" + }, + { + "filePath": "../../node_modules/@shopify/shopify-api/lib/types.d.ts", + "name": "January24", + "value": "2024-01" + }, + { + "filePath": "../../node_modules/@shopify/shopify-api/lib/types.d.ts", + "name": "Unstable", + "value": "unstable" + } + ] + }, "AdminOperations": { "filePath": "../../node_modules/@shopify/shopify-api/node_modules/@shopify/admin-api-client/dist/ts/graphql/types.d.ts", "syntaxKind": "TypeAliasDeclaration", @@ -18542,11 +19114,135 @@ "title": "app/routes/**\\/.ts" } ] + }, + { + "title": "Handling GraphQL errors", + "description": "Catch `GraphqlQueryError` errors to see error messages from the API.", + "tabs": [ + { + "code": "import { ActionFunctionArgs } from \"@remix-run/node\";\nimport { authenticate } from \"../shopify.server\";\n\nexport const action = async ({ request }: ActionFunctionArgs) => {\n const { storefront } = await authenticate.public.appProxy(request);\n\n try {\n const response = await storefront.graphql(\n `#graphql\n query incorrectQuery {\n products(first: 10) {\n nodes {\n not_a_field\n }\n }\n }`,\n );\n\n return json({ data: await response.json() });\n } catch (error) {\n if (error instanceof GraphqlQueryError) {\n // { errors: { graphQLErrors: [\n // { message: \"Field 'not_a_field' doesn't exist on type 'Product'\" }\n // ] } }\n return json({ errors: error.body?.errors }, { status: 500 });\n }\n return json({ message: \"An error occurred\" }, { status: 500 });\n }\n}", + "title": "/app/routes/**\\/*.ts" + }, + { + "code": "import { shopifyApp } from \"@shopify/shopify-app-remix/server\";\n\nconst shopify = shopifyApp({\n // ...\n});\nexport default shopify;\nexport const authenticate = shopify.authenticate;", + "title": "/app/shopify.server.ts" + } + ] } ] } ], - "value": "export interface StorefrontContext {\n /**\n * Method for interacting with the Shopify Storefront GraphQL API\n *\n * 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).\n *\n * {@link https://shopify.dev/docs/api/storefront}\n *\n * @example\n * Querying the GraphQL API.\n * Use `storefront.graphql` to make query / mutation requests.\n * ```ts\n * // app/routes/**\\/.ts\n * import { json } from \"@remix-run/node\";\n * import { authenticate } from \"../shopify.server\";\n *\n * export async function action({ request }: ActionFunctionArgs) {\n * const { storefront } = await authenticate.public.appProxy(request);\n *\n * const response = await storefront.graphql(`{blogs(first: 10) { edges { node { id } } } }`);\n *\n * return json(await response.json());\n * }\n * ```\n */\n graphql: GraphQLClient;\n}" + "value": "export interface StorefrontContext {\n /**\n * Method for interacting with the Shopify Storefront GraphQL API\n *\n * 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).\n *\n * {@link https://shopify.dev/docs/api/storefront}\n *\n * @example\n * Querying the GraphQL API.\n * Use `storefront.graphql` to make query / mutation requests.\n * ```ts\n * // app/routes/**\\/.ts\n * import { json } from \"@remix-run/node\";\n * import { authenticate } from \"../shopify.server\";\n *\n * export async function action({ request }: ActionFunctionArgs) {\n * const { storefront } = await authenticate.public.appProxy(request);\n *\n * const response = await storefront.graphql(`{blogs(first: 10) { edges { node { id } } } }`);\n *\n * return json(await response.json());\n * }\n * ```\n *\n * @example\n * Handling GraphQL errors.\n * Catch `GraphqlQueryError` errors to see error messages from the API.\n * ```ts\n * // /app/routes/**\\/*.ts\n * import { ActionFunctionArgs } from \"@remix-run/node\";\n * import { authenticate } from \"../shopify.server\";\n *\n * export const action = async ({ request }: ActionFunctionArgs) => {\n * const { storefront } = await authenticate.public.appProxy(request);\n *\n * try {\n * const response = await storefront.graphql(\n * `#graphql\n * query incorrectQuery {\n * products(first: 10) {\n * nodes {\n * not_a_field\n * }\n * }\n * }`,\n * );\n *\n * return json({ data: await response.json() });\n * } catch (error) {\n * if (error instanceof GraphqlQueryError) {\n * // { errors: { graphQLErrors: [\n * // { message: \"Field 'not_a_field' doesn't exist on type 'Product'\" }\n * // ] } }\n * return json({ errors: error.body?.errors }, { status: 500 });\n * }\n * return json({ message: \"An error occurred\" }, { status: 500 });\n * }\n * }\n * ```\n *\n * ```ts\n * // /app/shopify.server.ts\n * import { shopifyApp } from \"@shopify/shopify-app-remix/server\";\n *\n * const shopify = shopifyApp({\n * // ...\n * });\n * export default shopify;\n * export const authenticate = shopify.authenticate;\n * ```\n */\n graphql: GraphQLClient;\n}" + }, + "GraphQLClient": { + "filePath": "src/server/clients/types.ts", + "name": "GraphQLClient", + "description": "", + "params": [ + { + "name": "query", + "description": "", + "value": "Operation extends keyof Operations", + "filePath": "src/server/clients/types.ts" + }, + { + "name": "options", + "description": "", + "value": "GraphQLQueryOptions", + "isOptional": true, + "filePath": "src/server/clients/types.ts" + } + ], + "returns": { + "filePath": "src/server/clients/types.ts", + "description": "", + "name": "interface Promise {\n /**\n * Attaches callbacks for the resolution and/or rejection of the Promise.\n * @param onfulfilled The callback to execute when the Promise is resolved.\n * @param onrejected The callback to execute when the Promise is rejected.\n * @returns A Promise for the completion of which ever callback is executed.\n */\n then(onfulfilled?: ((value: T) => TResult1 | PromiseLike) | undefined | null, onrejected?: ((reason: any) => TResult2 | PromiseLike) | undefined | null): Promise;\n\n /**\n * Attaches a callback for only the rejection of the Promise.\n * @param onrejected The callback to execute when the Promise is rejected.\n * @returns A Promise for the completion of the callback.\n */\n catch(onrejected?: ((reason: any) => TResult | PromiseLike) | undefined | null): Promise;\n}, interface Promise {}, Promise: PromiseConstructor, interface Promise {\n readonly [Symbol.toStringTag]: string;\n}, interface Promise {\n /**\n * Attaches a callback that is invoked when the Promise is settled (fulfilled or rejected). The\n * resolved value cannot be modified from the callback.\n * @param onfinally The callback to execute when the Promise is settled (fulfilled or rejected).\n * @returns A Promise for the completion of the callback.\n */\n finally(onfinally?: (() => void) | undefined | null): Promise;\n}", + "value": "interface Promise {\n /**\n * Attaches callbacks for the resolution and/or rejection of the Promise.\n * @param onfulfilled The callback to execute when the Promise is resolved.\n * @param onrejected The callback to execute when the Promise is rejected.\n * @returns A Promise for the completion of which ever callback is executed.\n */\n then(onfulfilled?: ((value: T) => TResult1 | PromiseLike) | undefined | null, onrejected?: ((reason: any) => TResult2 | PromiseLike) | undefined | null): Promise;\n\n /**\n * Attaches a callback for only the rejection of the Promise.\n * @param onrejected The callback to execute when the Promise is rejected.\n * @returns A Promise for the completion of the callback.\n */\n catch(onrejected?: ((reason: any) => TResult | PromiseLike) | undefined | null): Promise;\n}, interface Promise {}, Promise: PromiseConstructor, interface Promise {\n readonly [Symbol.toStringTag]: string;\n}, interface Promise {\n /**\n * Attaches a callback that is invoked when the Promise is settled (fulfilled or rejected). The\n * resolved value cannot be modified from the callback.\n * @param onfinally The callback to execute when the Promise is settled (fulfilled or rejected).\n * @returns A Promise for the completion of the callback.\n */\n finally(onfinally?: (() => void) | undefined | null): Promise;\n}" + }, + "value": "export type GraphQLClient = <\n Operation extends keyof Operations,\n>(\n query: Operation,\n options?: GraphQLQueryOptions,\n) => Promise>;" + }, + "GraphQLQueryOptions": { + "filePath": "src/server/clients/types.ts", + "name": "GraphQLQueryOptions", + "description": "", + "members": [ + { + "filePath": "src/server/clients/types.ts", + "syntaxKind": "PropertySignature", + "name": "variables", + "value": "ApiClientRequestOptions[\"variables\"]", + "description": "The variables to pass to the operation.", + "isOptional": true + }, + { + "filePath": "src/server/clients/types.ts", + "syntaxKind": "PropertySignature", + "name": "apiVersion", + "value": "ApiVersion", + "description": "The version of the API to use for the request.", + "isOptional": true + }, + { + "filePath": "src/server/clients/types.ts", + "syntaxKind": "PropertySignature", + "name": "headers", + "value": "Record", + "description": "Additional headers to include in the request.", + "isOptional": true + }, + { + "filePath": "src/server/clients/types.ts", + "syntaxKind": "PropertySignature", + "name": "tries", + "value": "number", + "description": "The total number of times to try the request if it fails.", + "isOptional": true + } + ], + "value": "export interface GraphQLQueryOptions<\n Operation extends keyof Operations,\n Operations extends AllOperations,\n> {\n /**\n * The variables to pass to the operation.\n */\n variables?: ApiClientRequestOptions['variables'];\n /**\n * The version of the API to use for the request.\n */\n apiVersion?: ApiVersion;\n /**\n * Additional headers to include in the request.\n */\n headers?: Record;\n /**\n * The total number of times to try the request if it fails.\n */\n tries?: number;\n}" + }, + "ApiVersion": { + "filePath": "../../node_modules/@shopify/shopify-api/lib/types.d.ts", + "syntaxKind": "EnumDeclaration", + "name": "ApiVersion", + "value": "export declare enum ApiVersion {\n October22 = \"2022-10\",\n January23 = \"2023-01\",\n April23 = \"2023-04\",\n July23 = \"2023-07\",\n October23 = \"2023-10\",\n January24 = \"2024-01\",\n Unstable = \"unstable\"\n}", + "members": [ + { + "filePath": "../../node_modules/@shopify/shopify-api/lib/types.d.ts", + "name": "October22", + "value": "2022-10" + }, + { + "filePath": "../../node_modules/@shopify/shopify-api/lib/types.d.ts", + "name": "January23", + "value": "2023-01" + }, + { + "filePath": "../../node_modules/@shopify/shopify-api/lib/types.d.ts", + "name": "April23", + "value": "2023-04" + }, + { + "filePath": "../../node_modules/@shopify/shopify-api/lib/types.d.ts", + "name": "July23", + "value": "2023-07" + }, + { + "filePath": "../../node_modules/@shopify/shopify-api/lib/types.d.ts", + "name": "October23", + "value": "2023-10" + }, + { + "filePath": "../../node_modules/@shopify/shopify-api/lib/types.d.ts", + "name": "January24", + "value": "2024-01" + }, + { + "filePath": "../../node_modules/@shopify/shopify-api/lib/types.d.ts", + "name": "Unstable", + "value": "unstable" + } + ] }, "StorefrontOperations": { "filePath": "../../node_modules/@shopify/shopify-api/node_modules/@shopify/storefront-api-client/dist/storefront-api-client.d.ts", @@ -18641,6 +19337,24 @@ } ] } + }, + { + "description": "Catch `GraphqlQueryError` errors to see error messages from the API.", + "codeblock": { + "title": "Handling GraphQL errors", + "tabs": [ + { + "title": "/app/routes/**\\/*.ts", + "code": "import { ActionFunctionArgs } from \"@remix-run/node\";\nimport { authenticate } from \"../shopify.server\";\n\nexport const action = async ({ request }: ActionFunctionArgs) => {\n const { storefront } = await authenticate.public.appProxy(request);\n\n try {\n const response = await storefront.graphql(\n `#graphql\n query incorrectQuery {\n products(first: 10) {\n nodes {\n not_a_field\n }\n }\n }`,\n );\n\n return json({ data: await response.json() });\n } catch (error) {\n if (error instanceof GraphqlQueryError) {\n // { errors: { graphQLErrors: [\n // { message: \"Field 'not_a_field' doesn't exist on type 'Product'\" }\n // ] } }\n return json({ errors: error.body?.errors }, { status: 500 });\n }\n return json({ message: \"An error occurred\" }, { status: 500 });\n }\n}", + "language": "typescript" + }, + { + "title": "/app/shopify.server.ts", + "code": "import { shopifyApp } from \"@shopify/shopify-app-remix/server\";\n\nconst shopify = shopifyApp({\n // ...\n});\nexport default shopify;\nexport const authenticate = shopify.authenticate;", + "language": "typescript" + } + ] + } } ] } diff --git a/packages/shopify-app-remix/docs/generated/generated_static_pages.json b/packages/shopify-app-remix/docs/generated/generated_static_pages.json index bab990e35a..50ebab3a8e 100644 --- a/packages/shopify-app-remix/docs/generated/generated_static_pages.json +++ b/packages/shopify-app-remix/docs/generated/generated_static_pages.json @@ -164,6 +164,12 @@ "description": "Returns the same `admin` context (`AdminApiContext`) from `authenticate.webhook` that is returned from `authenticate.admin`.\n\nSee [authenticate.webhook](/docs/api/shopify-app-remix/authenticate/webhook#example-admin) for more details.", "isOptional": true }, + { + "name": "v3_lineItemBilling", + "value": "", + "description": "Allows you to pass billing plans with up to two line items when creating a new app subscription.\n\n This allows you to create app subscriptions with both recurring and usage based components.", + "isOptional": true + }, { "name": "unstable_newEmbeddedAuthStrategy", "value": "", @@ -518,7 +524,7 @@ "tabs": [ { "title": "/app/routes/webhooks.tsx", - "code": "import {ActionFunction} from '@remix-run/node';\n\nimport db from '../db.server';\n\nimport {authenticate} from '~/shopify.server';\n\nexport const action: ActionFunction = async ({request}) => {\n const {topic, shop, session} = await authenticate.webhook(request);\n\n switch (topic) {\n case 'APP_UNINSTALLED':\n if (session) {\n await db.session.deleteMany({where: {shop}});\n }\n break;\n case 'CUSTOMERS_DATA_REQUEST':\n case 'CUSTOMERS_REDACT':\n case 'SHOP_REDACT':\n default:\n throw new Response('Unhandled webhook topic', {status: 404});\n }\n\n throw new Response();\n};\n", + "code": "import {ActionFunctionArgs} from '@remix-run/node';\n\nimport db from '../db.server';\n\nimport {authenticate} from '~/shopify.server';\n\nexport const action = async ({request}: ActionFunctionArgs) => {\n const {topic, shop, session} = await authenticate.webhook(request);\n\n switch (topic) {\n case 'APP_UNINSTALLED':\n if (session) {\n await db.session.deleteMany({where: {shop}});\n }\n break;\n case 'CUSTOMERS_DATA_REQUEST':\n case 'CUSTOMERS_REDACT':\n case 'SHOP_REDACT':\n default:\n throw new Response('Unhandled webhook topic', {status: 404});\n }\n\n throw new Response();\n};\n", "language": "tsx" } ] diff --git a/packages/shopify-app-remix/docs/staticPages/examples/guides/webhooks/endpoint.example.ts b/packages/shopify-app-remix/docs/staticPages/examples/guides/webhooks/endpoint.example.ts index 51a48e8b9d..eca535cb46 100644 --- a/packages/shopify-app-remix/docs/staticPages/examples/guides/webhooks/endpoint.example.ts +++ b/packages/shopify-app-remix/docs/staticPages/examples/guides/webhooks/endpoint.example.ts @@ -1,10 +1,10 @@ -import {ActionFunction} from '@remix-run/node'; +import {ActionFunctionArgs} from '@remix-run/node'; import db from '../db.server'; import {authenticate} from '~/shopify.server'; -export const action: ActionFunction = async ({request}) => { +export const action = async ({request}: ActionFunctionArgs) => { const {topic, shop, session} = await authenticate.webhook(request); switch (topic) { diff --git a/packages/shopify-app-remix/src/server/authenticate/admin/types.ts b/packages/shopify-app-remix/src/server/authenticate/admin/types.ts index 96dedc4885..e83075929d 100644 --- a/packages/shopify-app-remix/src/server/authenticate/admin/types.ts +++ b/packages/shopify-app-remix/src/server/authenticate/admin/types.ts @@ -22,16 +22,6 @@ interface AdminContextInternal< * Using offline sessions. * Get your app's shop-specific data using an offline session. * ```ts - * // shopify.server.ts - * import { shopifyApp } from "@shopify/shopify-app-remix/server"; - * - * const shopify = shopifyApp({ - * // ...etc - * }); - * export default shopify; - * export const authenticate = shopify.authenticate; - * ``` - * ```ts * // /app/routes/**\/*.ts * import { LoaderFunctionArgs, json } from "@remix-run/node"; * import { authenticate } from "../shopify.server"; @@ -42,21 +32,20 @@ interface AdminContextInternal< * return json(await getMyAppData({shop: session.shop)); * }; * ``` - * - * @example - * Using online sessions. - * Get your app's user-specific data using an online session. * ```ts * // shopify.server.ts * import { shopifyApp } from "@shopify/shopify-app-remix/server"; * * const shopify = shopifyApp({ * // ...etc - * useOnlineTokens: true, * }); * export default shopify; * export const authenticate = shopify.authenticate; * ``` + * + * @example + * Using online sessions. + * Get your app's user-specific data using an online session. * ```ts * // /app/routes/**\/*.ts * import { LoaderFunctionArgs, json } from "@remix-run/node"; @@ -68,6 +57,17 @@ interface AdminContextInternal< * return json(await getMyAppData({user: session.onlineAccessInfo!.id})); * }; * ``` + * ```ts + * // shopify.server.ts + * import { shopifyApp } from "@shopify/shopify-app-remix/server"; + * + * const shopify = shopifyApp({ + * // ...etc + * useOnlineTokens: true, + * }); + * export default shopify; + * export const authenticate = shopify.authenticate; + * ``` */ session: Session; @@ -119,29 +119,29 @@ export interface EmbeddedAdminContext< * Using the decoded session token. * Get user-specific data using the `sessionToken` object. * ```ts - * // shopify.server.ts - * import { shopifyApp } from "@shopify/shopify-app-remix/server"; - * - * const shopify = shopifyApp({ - * // ...etc - * useOnlineTokens: true, - * }); - * export default shopify; - * export const authenticate = shopify.authenticate; - * ``` - * ```ts * // /app/routes/**\/*.ts * import { LoaderFunctionArgs, json } from "@remix-run/node"; * import { authenticate } from "../shopify.server"; * import { getMyAppData } from "~/db/model.server"; * * export const loader = async ({ request }: LoaderFunctionArgs) => { - * const { sessionToken } = await authenticate.public.checkout( + * const { sessionToken } = await authenticate.admin( * request * ); * return json(await getMyAppData({user: sessionToken.sub})); * }; * ``` + * ```ts + * // shopify.server.ts + * import { shopifyApp } from "@shopify/shopify-app-remix/server"; + * + * const shopify = shopifyApp({ + * // ...etc + * useOnlineTokens: true, + * }); + * export default shopify; + * export const authenticate = shopify.authenticate; + * ``` */ sessionToken: JwtPayload; diff --git a/packages/shopify-app-remix/src/server/authenticate/flow/types.ts b/packages/shopify-app-remix/src/server/authenticate/flow/types.ts index 6beba83b4c..e9ee8200ac 100644 --- a/packages/shopify-app-remix/src/server/authenticate/flow/types.ts +++ b/packages/shopify-app-remix/src/server/authenticate/flow/types.ts @@ -15,10 +15,10 @@ export interface FlowContext< * Use the session associated with this request to use REST resources. * ```ts * // /app/routes/flow.tsx - * import { ActionFunction } from "@remix-run/node"; + * import { ActionFunctionArgs } from "@remix-run/node"; * import { authenticate } from "../shopify.server"; * - * export const action: ActionFunction = async ({ request }) => { + * export const action = async ({ request }: ActionFunctionArgs) => { * const { session, admin } = await authenticate.flow(request); * * const products = await admin?.rest.resources.Product.all({ session }); @@ -38,10 +38,10 @@ export interface FlowContext< * Get the request's POST payload. * ```ts * // /app/routes/flow.tsx - * import { ActionFunction } from "@remix-run/node"; + * import { ActionFunctionArgs } from "@remix-run/node"; * import { authenticate } from "../shopify.server"; * - * export const action: ActionFunction = async ({ request }) => { + * export const action = async ({ request }: ActionFunctionArgs) => { * const { payload } = await authenticate.flow(request); * return new Response(); * }; diff --git a/packages/shopify-app-remix/src/server/authenticate/public/appProxy/authenticate.public.app-proxy.doc.ts b/packages/shopify-app-remix/src/server/authenticate/public/appProxy/authenticate.public.app-proxy.doc.ts index 5f811705f3..6fe00d5b78 100644 --- a/packages/shopify-app-remix/src/server/authenticate/public/appProxy/authenticate.public.app-proxy.doc.ts +++ b/packages/shopify-app-remix/src/server/authenticate/public/appProxy/authenticate.public.app-proxy.doc.ts @@ -30,6 +30,12 @@ const data: ReferenceEntityTemplateSchema = { subtitle: 'Interact with the Storefront API.', url: '/docs/api/shopify-app-remix/apis/storefront-api', }, + { + name: 'Liquid reference', + subtitle: "Use the shop's theme to render a template.", + url: '/docs/api/liquid', + type: 'liquid', + }, ], }; diff --git a/packages/shopify-app-remix/src/server/authenticate/public/appProxy/types.ts b/packages/shopify-app-remix/src/server/authenticate/public/appProxy/types.ts index ad1c8a0885..d03696982d 100644 --- a/packages/shopify-app-remix/src/server/authenticate/public/appProxy/types.ts +++ b/packages/shopify-app-remix/src/server/authenticate/public/appProxy/types.ts @@ -24,7 +24,7 @@ interface Context { * * @example * Rendering liquid content. - * Use the `liquid` helper to render a `Response` with Liquid content using the shop's theme. + * Use the `liquid` helper to render a `Response` with Liquid content using the shop's theme. See the [Liquid reference](https://shopify.dev/docs/api/liquid) for all the features it enables. * ```ts * // app/routes/**\/.ts * import {authenticate} from "~/shopify.server" @@ -98,10 +98,13 @@ export interface AppProxyContextWithSession< * * export const loader = async ({ request }) => { * // Get the session for the shop that initiated the request to the app proxy. - * const { session } = await authenticate.public.appProxy(request); + * const { session } = + * await authenticate.public.appProxy(request); * * // Use the session data to make to queries to your database or additional requests. - * return json(await getMyAppModelData({shop: session.shop)); + * return json( + * await getMyAppModelData({shop: session.shop}) + * ); * }; * ``` */ @@ -130,7 +133,11 @@ export interface AppProxyContextWithSession< * } * } * }`, - * { variables: { input: { title: "Product Name" } } } + * { + * variables: { + * input: { title: "Product Name" } + * } + * } * ); * * const productData = await response.json(); @@ -154,7 +161,18 @@ export interface AppProxyContextWithSession< * export async function action({ request }: ActionFunctionArgs) { * const { storefront } = await authenticate.public.appProxy(request); * - * const response = await storefront.graphql(`{blogs(first: 10) { edges { node { id } } } }`); + * const response = await storefront.graphql( + * `#graphql + * query blogIds { + * blogs(first: 10) { + * edges { + * node { + * id + * } + * } + * } + * }` + * ); * * return json(await response.json()); * } diff --git a/packages/shopify-app-remix/src/server/authenticate/webhooks/types.ts b/packages/shopify-app-remix/src/server/authenticate/webhooks/types.ts index 5e9c8b257a..41ac4d0ed0 100644 --- a/packages/shopify-app-remix/src/server/authenticate/webhooks/types.ts +++ b/packages/shopify-app-remix/src/server/authenticate/webhooks/types.ts @@ -20,10 +20,10 @@ interface Context { * Get the API version used for webhook request. * ```ts * // /app/routes/webhooks.tsx - * import { ActionFunction } from "@remix-run/node"; + * import { ActionFunctionArgs } from "@remix-run/node"; * import { authenticate } from "../shopify.server"; * - * export const action: ActionFunction = async ({ request }) => { + * export const action = async ({ request }: ActionFunctionArgs) => { * const { apiVersion } = await authenticate.webhook(request); * return new Response(); * }; @@ -38,10 +38,10 @@ interface Context { * Get the shop that triggered a webhook. * ```ts * // /app/routes/webhooks.tsx - * import { ActionFunction } from "@remix-run/node"; + * import { ActionFunctionArgs } from "@remix-run/node"; * import { authenticate } from "../shopify.server"; * - * export const action: ActionFunction = async ({ request }) => { + * export const action = async ({ request }: ActionFunctionArgs) => { * const { shop } = await authenticate.webhook(request); * return new Response(); * }; @@ -56,10 +56,10 @@ interface Context { * Get the event topic for the webhook. * ```ts * // /app/routes/webhooks.tsx - * import { ActionFunction } from "@remix-run/node"; + * import { ActionFunctionArgs } from "@remix-run/node"; * import { authenticate } from "../shopify.server"; * - * export const action: ActionFunction = async ({ request }) => { + * export const action = async ({ request }: ActionFunctionArgs) => { * const { topic } = await authenticate.webhook(request); * * switch (topic) { @@ -81,10 +81,10 @@ interface Context { * Get the webhook ID. * ```ts * // /app/routes/webhooks.tsx - * import { ActionFunction } from "@remix-run/node"; + * import { ActionFunctionArgs } from "@remix-run/node"; * import { authenticate } from "../shopify.server"; * - * export const action: ActionFunction = async ({ request }) => { + * export const action = async ({ request }: ActionFunctionArgs) => { * const { webhookId } = await authenticate.webhook(request); * return new Response(); * }; @@ -99,10 +99,10 @@ interface Context { * Get the request's POST payload. * ```ts * // /app/routes/webhooks.tsx - * import { ActionFunction } from "@remix-run/node"; + * import { ActionFunctionArgs } from "@remix-run/node"; * import { authenticate } from "../shopify.server"; * - * export const action: ActionFunction = async ({ request }) => { + * export const action = async ({ request }: ActionFunctionArgs) => { * const { payload } = await authenticate.webhook(request); * return new Response(); * }; @@ -117,10 +117,10 @@ interface Context { * Get the webhook sub-topic. * ```ts * // /app/routes/webhooks.tsx - * import { ActionFunction } from "@remix-run/node"; + * import { ActionFunctionArgs } from "@remix-run/node"; * import { authenticate } from "../shopify.server"; * - * export const action: ActionFunction = async ({ request }) => { + * export const action = async ({ request }: ActionFunctionArgs) => { * const { subTopic } = await authenticate.webhook(request); * return new Response(); * }; diff --git a/packages/shopify-app-remix/src/server/clients/admin/types.ts b/packages/shopify-app-remix/src/server/clients/admin/types.ts index 83688e44b0..c352d32e6a 100644 --- a/packages/shopify-app-remix/src/server/clients/admin/types.ts +++ b/packages/shopify-app-remix/src/server/clients/admin/types.ts @@ -38,12 +38,17 @@ export interface AdminApiContext< * import { authenticate } from "../shopify.server"; * * export const loader = async ({ request }: LoaderFunctionArgs) => { - * const { admin, session } = await authenticate.admin(request); - * return json(admin.rest.resources.Order.count({ session })); + * const { + * admin, + * session, + * } = await authenticate.admin(request); + * + * return json( + * admin.rest.resources.Order.count({ session }), + * ); * }; * ``` * - * * ```ts * // /app/shopify.server.ts * import { shopifyApp } from "@shopify/shopify-app-remix/server"; @@ -57,7 +62,6 @@ export interface AdminApiContext< * export const authenticate = shopify.authenticate; * ``` * - * * @example * Performing a GET request to the REST API. * Use `admin.rest.get` to make custom requests to make a request to to the `customer/count` endpoint @@ -68,9 +72,16 @@ export interface AdminApiContext< * import { authenticate } from "../shopify.server"; * * export const loader = async ({ request }: LoaderFunctionArgs) => { - * const { admin, session } = await authenticate.admin(request); - * const response = await admin.rest.get({ path: "/customers/count.json" }); + * const { + * admin, + * session, + * } = await authenticate.admin(request); + * + * const response = await admin.rest.get({ + * path: "/customers/count.json", + * }); * const customers = await response.json(); + * * return json({ customers }); * }; * ``` @@ -87,6 +98,7 @@ export interface AdminApiContext< * export default shopify; * export const authenticate = shopify.authenticate; * ``` + * * @example * Performing a POST request to the REST API. * Use `admin.rest.post` to make custom requests to make a request to to the `customers.json` endpoint to send a welcome email @@ -96,7 +108,11 @@ export interface AdminApiContext< * import { authenticate } from "../shopify.server"; * * export const loader = async ({ request }: LoaderFunctionArgs) => { - * const { admin, session } = await authenticate.admin(request); + * const { + * admin, + * session, + * } = await authenticate.admin(request); + * * const response = admin.rest.post({ * path: "customers/7392136888625/send_invite.json", * body: { @@ -108,7 +124,8 @@ export interface AdminApiContext< * custom_message: "My awesome new store", * }, * }, - * }); + * }); + * * const customerInvite = await response.json(); * return json({ customerInvite }); * }; @@ -139,10 +156,11 @@ export interface AdminApiContext< * Querying the GraphQL API. * Use `admin.graphql` to make query / mutation requests. * ```ts + * // /app/routes/**\/*.ts * import { ActionFunctionArgs } from "@remix-run/node"; * import { authenticate } from "../shopify.server"; * - * export async function action({ request }: ActionFunctionArgs) { + * export const action = async ({ request }: ActionFunctionArgs) => { * const { admin } = await authenticate.admin(request); * * const response = await admin.graphql( @@ -154,11 +172,17 @@ export interface AdminApiContext< * } * } * }`, - * { variables: { input: { title: "Product Name" } } } + * { + * variables: { + * input: { title: "Product Name" }, + * }, + * }, * ); * * const productData = await response.json(); - * return json({ data: productData.data }); + * return json({ + * productId: productData.data?.productCreate?.product?.id, + * }); * } * ``` * @@ -167,11 +191,58 @@ export interface AdminApiContext< * import { shopifyApp } from "@shopify/shopify-app-remix/server"; * * const shopify = shopifyApp({ - * restResources, - * // ...etc + * // ... * }); * export default shopify; - * export const unauthenticated = shopify.unauthenticated; + * export const authenticate = shopify.authenticate; + * ``` + * + * @example + * Handling GraphQL errors. + * Catch `GraphqlQueryError` errors to see error messages from the API. + * ```ts + * // /app/routes/**\/*.ts + * import { ActionFunctionArgs } from "@remix-run/node"; + * import { authenticate } from "../shopify.server"; + * + * export const action = async ({ request }: ActionFunctionArgs) => { + * const { admin } = await authenticate.admin(request); + * + * try { + * const response = await admin.graphql( + * `#graphql + * query incorrectQuery { + * products(first: 10) { + * nodes { + * not_a_field + * } + * } + * }`, + * ); + * + * return json({ data: await response.json() }); + * } catch (error) { + * if (error instanceof GraphqlQueryError) { + * // error.body.errors: + * // { graphQLErrors: [ + * // { message: "Field 'not_a_field' doesn't exist on type 'Product'" } + * // ] } + * return json({ errors: error.body?.errors }, { status: 500 }); + * } + * return json({ message: "An error occurred" }, { status: 500 }); + * } + * } + * ``` + * + * ```ts + * // /app/shopify.server.ts + * import { shopifyApp } from "@shopify/shopify-app-remix/server"; + * + * const shopify = shopifyApp({ + * // ... + * }); + * export default shopify; + * export const authenticate = shopify.authenticate; * ``` */ graphql: GraphQLClient; diff --git a/packages/shopify-app-remix/src/server/clients/storefront/types.ts b/packages/shopify-app-remix/src/server/clients/storefront/types.ts index 8e22eb9b7d..048c50d98d 100644 --- a/packages/shopify-app-remix/src/server/clients/storefront/types.ts +++ b/packages/shopify-app-remix/src/server/clients/storefront/types.ts @@ -26,6 +26,53 @@ export interface StorefrontContext { * return json(await response.json()); * } * ``` + * + * @example + * Handling GraphQL errors. + * Catch `GraphqlQueryError` errors to see error messages from the API. + * ```ts + * // /app/routes/**\/*.ts + * import { ActionFunctionArgs } from "@remix-run/node"; + * import { authenticate } from "../shopify.server"; + * + * export const action = async ({ request }: ActionFunctionArgs) => { + * const { storefront } = await authenticate.public.appProxy(request); + * + * try { + * const response = await storefront.graphql( + * `#graphql + * query incorrectQuery { + * products(first: 10) { + * nodes { + * not_a_field + * } + * } + * }`, + * ); + * + * return json({ data: await response.json() }); + * } catch (error) { + * if (error instanceof GraphqlQueryError) { + * // { errors: { graphQLErrors: [ + * // { message: "Field 'not_a_field' doesn't exist on type 'Product'" } + * // ] } } + * return json({ errors: error.body?.errors }, { status: 500 }); + * } + * return json({ message: "An error occurred" }, { status: 500 }); + * } + * } + * ``` + * + * ```ts + * // /app/shopify.server.ts + * import { shopifyApp } from "@shopify/shopify-app-remix/server"; + * + * const shopify = shopifyApp({ + * // ... + * }); + * export default shopify; + * export const authenticate = shopify.authenticate; + * ``` */ graphql: GraphQLClient; } diff --git a/packages/shopify-app-remix/src/server/clients/types.ts b/packages/shopify-app-remix/src/server/clients/types.ts index c6d58ca07b..97bd7b334a 100644 --- a/packages/shopify-app-remix/src/server/clients/types.ts +++ b/packages/shopify-app-remix/src/server/clients/types.ts @@ -11,17 +11,32 @@ export interface GraphQLQueryOptions< Operation extends keyof Operations, Operations extends AllOperations, > { + /** + * The variables to pass to the operation. + */ variables?: ApiClientRequestOptions['variables']; + /** + * The version of the API to use for the request. + */ apiVersion?: ApiVersion; + /** + * Additional headers to include in the request. + */ headers?: Record; + /** + * The total number of times to try the request if it fails. + */ tries?: number; } +export type GraphQLResponse< + Operation extends keyof Operations, + Operations extends AllOperations, +> = ResponseWithType>>; + export type GraphQLClient = < Operation extends keyof Operations, >( query: Operation, options?: GraphQLQueryOptions, -) => Promise< - ResponseWithType>> ->; +) => Promise>;