diff --git a/.changeset/brave-points-cross.md b/.changeset/brave-points-cross.md new file mode 100644 index 0000000000..9050b38c6f --- /dev/null +++ b/.changeset/brave-points-cross.md @@ -0,0 +1,5 @@ +--- +'@shopify/shopify-app-remix': patch +--- + +Fixes a bug that was causing external redirects to fail in remix actions diff --git a/.changeset/dirty-starfishes-accept.md b/.changeset/dirty-starfishes-accept.md new file mode 100644 index 0000000000..c7b5848bd5 --- /dev/null +++ b/.changeset/dirty-starfishes-accept.md @@ -0,0 +1,5 @@ +--- +'@shopify/shopify-app-remix': patch +--- + +Minor refactor in login.ts to use new URL util method from shopify-api-js diff --git a/.changeset/dry-meals-hear.md b/.changeset/dry-meals-hear.md new file mode 100644 index 0000000000..1790d7408c --- /dev/null +++ b/.changeset/dry-meals-hear.md @@ -0,0 +1,17 @@ +--- +'@shopify/shopify-app-session-storage-postgresql': patch +'@shopify/shopify-app-session-storage-test-utils': patch +'@shopify/shopify-app-session-storage-dynamodb': patch +'@shopify/shopify-app-session-storage-mongodb': patch +'@shopify/shopify-app-session-storage-memory': patch +'@shopify/shopify-app-session-storage-prisma': patch +'@shopify/shopify-app-session-storage-sqlite': patch +'@shopify/shopify-app-session-storage-mysql': patch +'@shopify/shopify-app-session-storage-redis': patch +'@shopify/shopify-app-session-storage-kv': patch +'@shopify/shopify-app-session-storage': patch +'@shopify/shopify-app-express': patch +'@shopify/shopify-app-remix': patch +--- + +Improved and simplified package.json dependencies diff --git a/.changeset/famous-knives-fix.md b/.changeset/famous-knives-fix.md new file mode 100644 index 0000000000..179ac9dd3e --- /dev/null +++ b/.changeset/famous-knives-fix.md @@ -0,0 +1,17 @@ +--- +'@shopify/shopify-app-session-storage-postgresql': patch +'@shopify/shopify-app-session-storage-test-utils': patch +'@shopify/shopify-app-session-storage-dynamodb': patch +'@shopify/shopify-app-session-storage-mongodb': patch +'@shopify/shopify-app-session-storage-memory': patch +'@shopify/shopify-app-session-storage-prisma': patch +'@shopify/shopify-app-session-storage-sqlite': patch +'@shopify/shopify-app-session-storage-mysql': patch +'@shopify/shopify-app-session-storage-redis': patch +'@shopify/shopify-app-session-storage-kv': patch +'@shopify/shopify-app-session-storage': patch +'@shopify/shopify-app-express': patch +'@shopify/shopify-app-remix': patch +--- + +Bump shopify-api version from 9.0.1 to 9.0.2 diff --git a/.changeset/ninety-rivers-worry.md b/.changeset/ninety-rivers-worry.md new file mode 100644 index 0000000000..3ad7602ff9 --- /dev/null +++ b/.changeset/ninety-rivers-worry.md @@ -0,0 +1,5 @@ +--- +'@shopify/shopify-app-session-storage-prisma': major +--- + +Updated the dependency on `prisma` to v5+. This package itself has no breaking changes, but you'll need to update your app's dependency on Prisma as well as this package. diff --git a/.changeset/pink-horses-unite.md b/.changeset/pink-horses-unite.md new file mode 100644 index 0000000000..2bb64e4289 --- /dev/null +++ b/.changeset/pink-horses-unite.md @@ -0,0 +1,5 @@ +--- +'@shopify/shopify-app-remix': minor +--- + +Add new embedded authorization strategy relying on Shopify managed install and OAuth token exchange diff --git a/.changeset/polite-birds-unite.md b/.changeset/polite-birds-unite.md new file mode 100644 index 0000000000..abe6780eff --- /dev/null +++ b/.changeset/polite-birds-unite.md @@ -0,0 +1,5 @@ +--- +'@shopify/shopify-app-express': patch +--- + +Replaced query() internal call in the GraphQL client with request() diff --git a/.changeset/smart-windows-smash.md b/.changeset/smart-windows-smash.md new file mode 100644 index 0000000000..a594b6f341 --- /dev/null +++ b/.changeset/smart-windows-smash.md @@ -0,0 +1,5 @@ +--- +'@shopify/shopify-app-remix': patch +--- + +Handle webhook registration throttling error diff --git a/.changeset/stale-glasses-reply.md b/.changeset/stale-glasses-reply.md new file mode 100644 index 0000000000..a845151cc8 --- /dev/null +++ b/.changeset/stale-glasses-reply.md @@ -0,0 +1,2 @@ +--- +--- diff --git a/.changeset/wild-bottles-explode.md b/.changeset/wild-bottles-explode.md new file mode 100644 index 0000000000..522fd6509e --- /dev/null +++ b/.changeset/wild-bottles-explode.md @@ -0,0 +1,5 @@ +--- +'@shopify/shopify-app-remix': patch +--- + +Use 'body' field from GraphqlQueryError when logging session validation error diff --git a/package.json b/package.json index b70701b160..fc2ef0184b 100644 --- a/package.json +++ b/package.json @@ -11,8 +11,8 @@ "clean": "rimraf ./packages/*/build .loom" }, "devDependencies": { - "@changesets/cli": "^2.26.1", - "@jest/types": "^29.6.1", + "@changesets/cli": "^2.27.1", + "@jest/types": "^29.6.3", "@shopify/eslint-plugin": "^42.1.0", "@shopify/loom": "^1.0.2", "@shopify/loom-cli": "^1.1.0", @@ -22,12 +22,16 @@ "@shopify/loom-plugin-prettier": "^2.0.1", "@shopify/prettier-config": "^1.1.2", "@shopify/typescript-configs": "^5.1.0", + "@types/jest": "^29.5.1", "eslint": "^8.55.0", + "eslint-plugin-prettier": "^4.2.1", "jest": "^29.1.0", "jest-fetch-mock": "^3.0.3", "jest-runner-eslint": "^2.0.0", + "prettier": "^2.8.8", "rimraf": "^5.0.0", "ts-jest": "^29.1.0", + "tslib": "^2.6.2", "typescript": "^4.9.5" }, "dependencies": {}, diff --git a/packages/shopify-app-express/package.json b/packages/shopify-app-express/package.json index 08de2a6551..ccd8159dda 100644 --- a/packages/shopify-app-express/package.json +++ b/packages/shopify-app-express/package.json @@ -30,27 +30,20 @@ "Storefront API" ], "dependencies": { - "@shopify/shopify-api": "^9.0.1", + "@shopify/shopify-api": "^9.0.2", "@shopify/shopify-app-session-storage": "^2.0.3", "@shopify/shopify-app-session-storage-memory": "^2.0.3", "cookie-parser": "^1.4.6", "express": "^4.18.1", - "semver": "^7.5.4", - "tslib": "^2.6.2" + "semver": "^7.5.4" }, "devDependencies": { - "@shopify/eslint-plugin": "^42.1.0", - "@shopify/prettier-config": "^1.1.2", "@types/compression": "^1.7.2", "@types/cookie-parser": "^1.4.3", "@types/express": "^4.17.16", "@types/jsonwebtoken": "^9.0.5", - "eslint": "^8.55.0", - "eslint-plugin-prettier": "^4.2.1", "jsonwebtoken": "^9.0.2", - "prettier": "^2.8.8", - "supertest": "^6.3.3", - "typescript": "^4.9.5" + "supertest": "^6.3.3" }, "files": [ "build/*", diff --git a/packages/shopify-app-express/src/middlewares/has-valid-access-token.ts b/packages/shopify-app-express/src/middlewares/has-valid-access-token.ts index 795e71f6de..a033cf3500 100644 --- a/packages/shopify-app-express/src/middlewares/has-valid-access-token.ts +++ b/packages/shopify-app-express/src/middlewares/has-valid-access-token.ts @@ -12,7 +12,7 @@ export async function hasValidAccessToken( ): Promise { try { const client = new api.clients.Graphql({session}); - await client.query({data: TEST_GRAPHQL_QUERY}); + await client.request(TEST_GRAPHQL_QUERY); return true; } catch (error) { if (error instanceof HttpResponseError && error.response.code === 401) { diff --git a/packages/shopify-app-remix/docs/build-docs.sh b/packages/shopify-app-remix/docs/build-docs.sh index 553121febe..1dee19ab41 100644 --- a/packages/shopify-app-remix/docs/build-docs.sh +++ b/packages/shopify-app-remix/docs/build-docs.sh @@ -1,10 +1,10 @@ -COMPILE_DOCS="yarn tsc --project docs/tsconfig.docs.json --types react --moduleResolution node --target esNext --module CommonJS && generate-docs --overridePath ./docs/typeOverride.json --input ./src --output ./docs/generated && find . -name '*.doc.js' -delete" -COMPILE_STATIC_PAGES="yarn tsc docs/staticPages/*.doc.ts --types react --moduleResolution node --target esNext --module CommonJS && generate-docs --isLandingPage --input ./docs/staticPages --output ./docs/generated && rm -rf docs/staticPages/*.doc.js" +COMPILE_DOCS="yarn tsc --project docs/tsconfig.docs.json --types react --moduleResolution node --target esNext --module CommonJS && generate-docs --overridePath ./docs/typeOverride.json --input ./src --declarationPath ../../node_modules/@shopify/shopify-api ../../node_modules/@shopify/polaris/build/ts --output ./docs/generated && find . -name '*.doc.js' -delete" +COMPILE_STATIC_PAGES="yarn tsc docs/staticPages/*.doc.ts --types react --moduleResolution node --target esNext --module CommonJS && generate-docs --isLandingPage --input ./docs/staticPages --declarationPath ../../node_modules/@shopify/shopify-api ../../node_modules/@shopify/polaris/build/ts --output ./docs/generated && rm -rf docs/staticPages/*.doc.js" if [ "$1" = "isTest" ]; then -COMPILE_DOCS="yarn tsc --project docs/tsconfig.docs.json --types react --moduleResolution node --target esNext --module CommonJS && generate-docs --overridePath ./docs/typeOverride.json --input ./src --output ./docs/temp && find . -name '*.doc.js' -delete" -COMPILE_STATIC_PAGES="yarn tsc docs/staticPages/*.doc.ts --types react --moduleResolution node --target esNext --module CommonJS && generate-docs --isLandingPage --input ./docs/staticPages --output ./docs/temp && rm -rf docs/staticPages/*.doc.js" +COMPILE_DOCS="yarn tsc --project docs/tsconfig.docs.json --types react --moduleResolution node --target esNext --module CommonJS && generate-docs --overridePath ./docs/typeOverride.json --input ./src --declarationPath ../../node_modules/@shopify/shopify-api ../../node_modules/@shopify/polaris/build/ts --output ./docs/temp && find . -name '*.doc.js' -delete" +COMPILE_STATIC_PAGES="yarn tsc docs/staticPages/*.doc.ts --types react --moduleResolution node --target esNext --module CommonJS && generate-docs --isLandingPage --input ./docs/staticPages --declarationPath ../../node_modules/@shopify/shopify-api ../../node_modules/@shopify/polaris/build/ts --output ./docs/temp && rm -rf docs/staticPages/*.doc.js" fi eval $COMPILE_DOCS 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 eecf09ad23..7d94cf2021 100644 --- a/packages/shopify-app-remix/docs/generated/generated_docs_data.json +++ b/packages/shopify-app-remix/docs/generated/generated_docs_data.json @@ -12,44 +12,27 @@ "type": "AppProviderProps", "typeDefinitions": { "AppProviderProps": { - "filePath": "/react/components/AppProvider/AppProvider.tsx", + "filePath": "../../node_modules/@shopify/polaris/build/ts/src/components/AppProvider/AppProvider.d.ts", "name": "AppProviderProps", "description": "", "members": [ { - "filePath": "/react/components/AppProvider/AppProvider.tsx", - "syntaxKind": "PropertySignature", - "name": "apiKey", - "value": "string", - "description": "The API key for your Shopify app. This is the `Client ID` from the Partner Dashboard.\n\nWhen using the Shopify CLI, this is the `SHOPIFY_API_KEY` environment variable. If you're using the environment variable, then you need to pass it from the loader to the component." - }, - { - "filePath": "/react/components/AppProvider/AppProvider.tsx", - "syntaxKind": "PropertySignature", - "name": "isEmbeddedApp", - "value": "boolean", - "description": "Whether the app is loaded inside the Shopify Admin. Default is `true`.\n\n\n\n\n", - "isOptional": true - }, - { - "filePath": "/react/components/AppProvider/AppProvider.tsx", + "filePath": "../../node_modules/@shopify/polaris/build/ts/src/components/AppProvider/AppProvider.d.ts", "syntaxKind": "PropertySignature", "name": "i18n", "value": "TranslationDictionary | TranslationDictionary[]", - "description": "The internationalization (i18n) configuration for your Polaris provider.\n\n\n\n\n", - "isOptional": true + "description": "A locale object or array of locale objects that overrides default translations. If specifying an array then your primary language dictionary should come first, followed by your fallback language dictionaries" }, { - "filePath": "/react/components/AppProvider/AppProvider.tsx", + "filePath": "../../node_modules/@shopify/polaris/build/ts/src/components/AppProvider/AppProvider.d.ts", "syntaxKind": "PropertySignature", - "name": "__APP_BRIDGE_URL", - "value": "string", - "description": "Used internally by Shopify. You don't need to set this.", - "isOptional": true, - "isPrivate": true + "name": "linkComponent", + "value": "LinkLikeComponent", + "description": "A custom component to use for all links used by Polaris components", + "isOptional": true }, { - "filePath": "/react/components/AppProvider/AppProvider.tsx", + "filePath": "../../node_modules/@shopify/polaris/build/ts/src/components/AppProvider/AppProvider.d.ts", "syntaxKind": "PropertySignature", "name": "features", "value": "FeaturesConfig", @@ -57,7 +40,7 @@ "isOptional": true }, { - "filePath": "/react/components/AppProvider/AppProvider.tsx", + "filePath": "../../node_modules/@shopify/polaris/build/ts/src/components/AppProvider/AppProvider.d.ts", "syntaxKind": "PropertySignature", "name": "children", "value": "React.ReactNode", @@ -65,7 +48,56 @@ "isOptional": true } ], - "value": "export interface AppProviderProps\n extends Omit {\n /**\n * The API key for your Shopify app. This is the `Client ID` from the Partner Dashboard.\n *\n * When using the Shopify CLI, this is the `SHOPIFY_API_KEY` environment variable. If you're using the environment\n * variable, then you need to pass it from the loader to the component.\n */\n apiKey: string;\n /**\n * Whether the app is loaded inside the Shopify Admin. Default is `true`.\n *\n * {@link https://shopify.dev/docs/apps/admin/embedded-app-home}\n */\n isEmbeddedApp?: boolean;\n /**\n * The internationalization (i18n) configuration for your Polaris provider.\n *\n * {@link https://polaris.shopify.com/components/utilities/app-provider}\n */\n i18n?: PolarisAppProviderProps['i18n'];\n /**\n * Used internally by Shopify. You don't need to set this.\n * @private\n */\n __APP_BRIDGE_URL?: string;\n}" + "value": "export interface AppProviderProps {\n /** A locale object or array of locale objects that overrides default translations. If specifying an array then your primary language dictionary should come first, followed by your fallback language dictionaries */\n i18n: ConstructorParameters[0];\n /** A custom component to use for all links used by Polaris components */\n linkComponent?: LinkLikeComponent;\n /** For toggling features */\n features?: FeaturesConfig;\n /** Inner content of the application */\n children?: React.ReactNode;\n}" + }, + "TranslationDictionary": { + "filePath": "../../node_modules/@shopify/polaris/build/ts/src/utilities/i18n/I18n.d.ts", + "name": "TranslationDictionary", + "description": "", + "members": [ + { + "filePath": "../../node_modules/@shopify/polaris/build/ts/src/utilities/i18n/I18n.d.ts", + "name": "[key: string]", + "value": "string | TranslationDictionary" + } + ], + "value": "interface TranslationDictionary {\n [key: string]: string | TranslationDictionary;\n}" + }, + "LinkLikeComponent": { + "filePath": "../../node_modules/@shopify/polaris/build/ts/src/utilities/link/types.d.ts", + "syntaxKind": "TypeAliasDeclaration", + "name": "LinkLikeComponent", + "value": "LinkLikeComponent", + "description": "" + }, + "FeaturesConfig": { + "filePath": "../../node_modules/@shopify/polaris/build/ts/src/utilities/features/types.d.ts", + "name": "FeaturesConfig", + "description": "", + "members": [ + { + "filePath": "../../node_modules/@shopify/polaris/build/ts/src/utilities/features/types.d.ts", + "name": "[key: string]", + "value": "boolean | undefined" + }, + { + "filePath": "../../node_modules/@shopify/polaris/build/ts/src/utilities/features/types.d.ts", + "syntaxKind": "PropertySignature", + "name": "polarisSummerEditions2023", + "value": "boolean", + "description": "", + "isOptional": true + }, + { + "filePath": "../../node_modules/@shopify/polaris/build/ts/src/utilities/features/types.d.ts", + "syntaxKind": "PropertySignature", + "name": "polarisSummerEditions2023ShadowBevelOptOut", + "value": "boolean", + "description": "", + "isOptional": true + } + ], + "value": "export interface FeaturesConfig {\n polarisSummerEditions2023?: boolean;\n polarisSummerEditions2023ShadowBevelOptOut?: boolean;\n [key: string]: boolean | undefined;\n}" } } } @@ -132,7 +164,7 @@ "type": "AuthenticateAdmin", "typeDefinitions": { "AuthenticateAdmin": { - "filePath": "/server/authenticate/admin/types.ts", + "filePath": "src/server/authenticate/admin/types.ts", "name": "AuthenticateAdmin", "description": "", "params": [ @@ -140,11 +172,11 @@ "name": "request", "description": "", "value": "Request", - "filePath": "/server/authenticate/admin/types.ts" + "filePath": "src/server/authenticate/admin/types.ts" } ], "returns": { - "filePath": "/server/authenticate/admin/types.ts", + "filePath": "src/server/authenticate/admin/types.ts", "description": "", "name": "Promise>", "value": "Promise>" @@ -152,19 +184,19 @@ "value": "export type AuthenticateAdmin<\n Config extends AppConfigArg,\n Resources extends ShopifyRestResources = ShopifyRestResources,\n> = (request: Request) => Promise>;" }, "AdminContext": { - "filePath": "/server/authenticate/admin/types.ts", + "filePath": "src/server/authenticate/admin/types.ts", "syntaxKind": "TypeAliasDeclaration", "name": "AdminContext", "value": "Config['isEmbeddedApp'] extends false\n ? NonEmbeddedAdminContext\n : EmbeddedAdminContext", "description": "" }, "NonEmbeddedAdminContext": { - "filePath": "/server/authenticate/admin/types.ts", + "filePath": "src/server/authenticate/admin/types.ts", "name": "NonEmbeddedAdminContext", "description": "", "members": [ { - "filePath": "/server/authenticate/admin/types.ts", + "filePath": "src/server/authenticate/admin/types.ts", "syntaxKind": "PropertySignature", "name": "session", "value": "Session", @@ -201,21 +233,21 @@ ] }, { - "filePath": "/server/authenticate/admin/types.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": "/server/authenticate/admin/types.ts", + "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": "/server/authenticate/admin/types.ts", + "filePath": "src/server/authenticate/admin/types.ts", "syntaxKind": "PropertySignature", "name": "cors", "value": "EnsureCORSFunction", @@ -236,13 +268,252 @@ ], "value": "export interface NonEmbeddedAdminContext<\n Config extends AppConfigArg,\n Resources extends ShopifyRestResources = ShopifyRestResources,\n> extends AdminContextInternal {}" }, + "Session": { + "filePath": "../../node_modules/@shopify/shopify-api/lib/session/session.d.ts", + "name": "Session", + "description": "Stores App information from logged in merchants so they can make authenticated requests to the Admin API.", + "members": [ + { + "filePath": "../../node_modules/@shopify/shopify-api/lib/session/session.d.ts", + "syntaxKind": "PropertyDeclaration", + "name": "id", + "value": "string", + "description": "" + }, + { + "filePath": "../../node_modules/@shopify/shopify-api/lib/session/session.d.ts", + "syntaxKind": "PropertyDeclaration", + "name": "shop", + "value": "string", + "description": "" + }, + { + "filePath": "../../node_modules/@shopify/shopify-api/lib/session/session.d.ts", + "syntaxKind": "PropertyDeclaration", + "name": "state", + "value": "string", + "description": "" + }, + { + "filePath": "../../node_modules/@shopify/shopify-api/lib/session/session.d.ts", + "syntaxKind": "PropertyDeclaration", + "name": "isOnline", + "value": "boolean", + "description": "" + }, + { + "filePath": "../../node_modules/@shopify/shopify-api/lib/session/session.d.ts", + "syntaxKind": "PropertyDeclaration", + "name": "scope", + "value": "string", + "description": "" + }, + { + "filePath": "../../node_modules/@shopify/shopify-api/lib/session/session.d.ts", + "syntaxKind": "PropertyDeclaration", + "name": "expires", + "value": "Date", + "description": "" + }, + { + "filePath": "../../node_modules/@shopify/shopify-api/lib/session/session.d.ts", + "syntaxKind": "PropertyDeclaration", + "name": "accessToken", + "value": "string", + "description": "" + }, + { + "filePath": "../../node_modules/@shopify/shopify-api/lib/session/session.d.ts", + "syntaxKind": "PropertyDeclaration", + "name": "onlineAccessInfo", + "value": "OnlineAccessInfo", + "description": "" + }, + { + "filePath": "../../node_modules/@shopify/shopify-api/lib/session/session.d.ts", + "syntaxKind": "MethodDeclaration", + "name": "isActive", + "value": "(scopes: string | string[] | AuthScopes) => boolean", + "description": "" + }, + { + "filePath": "../../node_modules/@shopify/shopify-api/lib/session/session.d.ts", + "syntaxKind": "MethodDeclaration", + "name": "isScopeChanged", + "value": "(scopes: string | string[] | AuthScopes) => boolean", + "description": "" + }, + { + "filePath": "../../node_modules/@shopify/shopify-api/lib/session/session.d.ts", + "syntaxKind": "MethodDeclaration", + "name": "isExpired", + "value": "(withinMillisecondsOfExpiry?: number) => boolean", + "description": "" + }, + { + "filePath": "../../node_modules/@shopify/shopify-api/lib/session/session.d.ts", + "syntaxKind": "MethodDeclaration", + "name": "toObject", + "value": "() => SessionParams", + "description": "" + }, + { + "filePath": "../../node_modules/@shopify/shopify-api/lib/session/session.d.ts", + "syntaxKind": "MethodDeclaration", + "name": "equals", + "value": "(other: Session) => boolean", + "description": "" + }, + { + "filePath": "../../node_modules/@shopify/shopify-api/lib/session/session.d.ts", + "syntaxKind": "MethodDeclaration", + "name": "toPropertyArray", + "value": "() => [string, string | number | boolean][]", + "description": "" + } + ], + "value": "export declare class Session {\n static fromPropertyArray(entries: [string, string | number | boolean][]): Session;\n readonly id: string;\n shop: string;\n state: string;\n isOnline: boolean;\n scope?: string;\n expires?: Date;\n accessToken?: string;\n onlineAccessInfo?: OnlineAccessInfo;\n constructor(params: SessionParams);\n isActive(scopes: AuthScopes | string | string[]): boolean;\n isScopeChanged(scopes: AuthScopes | string | string[]): boolean;\n isExpired(withinMillisecondsOfExpiry?: number): boolean;\n toObject(): SessionParams;\n equals(other: Session | undefined): boolean;\n toPropertyArray(): [string, string | number | boolean][];\n}" + }, + "OnlineAccessInfo": { + "filePath": "../../node_modules/@shopify/shopify-api/lib/auth/oauth/types.d.ts", + "name": "OnlineAccessInfo", + "description": "", + "members": [ + { + "filePath": "../../node_modules/@shopify/shopify-api/lib/auth/oauth/types.d.ts", + "syntaxKind": "PropertySignature", + "name": "expires_in", + "value": "number", + "description": "" + }, + { + "filePath": "../../node_modules/@shopify/shopify-api/lib/auth/oauth/types.d.ts", + "syntaxKind": "PropertySignature", + "name": "associated_user_scope", + "value": "string", + "description": "" + }, + { + "filePath": "../../node_modules/@shopify/shopify-api/lib/auth/oauth/types.d.ts", + "syntaxKind": "PropertySignature", + "name": "associated_user", + "value": "{ id: number; first_name: string; last_name: string; email: string; email_verified: boolean; account_owner: boolean; locale: string; collaborator: boolean; }", + "description": "" + } + ], + "value": "export interface OnlineAccessInfo {\n expires_in: number;\n associated_user_scope: string;\n associated_user: {\n id: number;\n first_name: string;\n last_name: string;\n email: string;\n email_verified: boolean;\n account_owner: boolean;\n locale: string;\n collaborator: boolean;\n };\n}" + }, + "AuthScopes": { + "filePath": "../../node_modules/@shopify/shopify-api/lib/auth/scopes/index.d.ts", + "name": "AuthScopes", + "description": "", + "members": [ + { + "filePath": "../../node_modules/@shopify/shopify-api/lib/auth/scopes/index.d.ts", + "syntaxKind": "MethodDeclaration", + "name": "has", + "value": "(scope: string | string[] | AuthScopes) => boolean", + "description": "" + }, + { + "filePath": "../../node_modules/@shopify/shopify-api/lib/auth/scopes/index.d.ts", + "syntaxKind": "MethodDeclaration", + "name": "equals", + "value": "(otherScopes: string | string[] | AuthScopes) => boolean", + "description": "" + }, + { + "filePath": "../../node_modules/@shopify/shopify-api/lib/auth/scopes/index.d.ts", + "syntaxKind": "MethodDeclaration", + "name": "toString", + "value": "() => string", + "description": "" + }, + { + "filePath": "../../node_modules/@shopify/shopify-api/lib/auth/scopes/index.d.ts", + "syntaxKind": "MethodDeclaration", + "name": "toArray", + "value": "() => string[]", + "description": "" + } + ], + "value": "declare class AuthScopes {\n static SCOPE_DELIMITER: string;\n private compressedScopes;\n private expandedScopes;\n constructor(scopes: string | string[] | AuthScopes | undefined);\n has(scope: string | string[] | AuthScopes | undefined): boolean;\n equals(otherScopes: string | string[] | AuthScopes | undefined): boolean;\n toString(): string;\n toArray(): string[];\n private getImpliedScopes;\n}" + }, + "SessionParams": { + "filePath": "../../node_modules/@shopify/shopify-api/lib/session/types.d.ts", + "name": "SessionParams", + "description": "", + "members": [ + { + "filePath": "../../node_modules/@shopify/shopify-api/lib/session/types.d.ts", + "syntaxKind": "PropertySignature", + "name": "id", + "value": "string", + "description": "" + }, + { + "filePath": "../../node_modules/@shopify/shopify-api/lib/session/types.d.ts", + "syntaxKind": "PropertySignature", + "name": "shop", + "value": "string", + "description": "" + }, + { + "filePath": "../../node_modules/@shopify/shopify-api/lib/session/types.d.ts", + "syntaxKind": "PropertySignature", + "name": "state", + "value": "string", + "description": "" + }, + { + "filePath": "../../node_modules/@shopify/shopify-api/lib/session/types.d.ts", + "syntaxKind": "PropertySignature", + "name": "isOnline", + "value": "boolean", + "description": "" + }, + { + "filePath": "../../node_modules/@shopify/shopify-api/lib/session/types.d.ts", + "syntaxKind": "PropertySignature", + "name": "scope", + "value": "string", + "description": "", + "isOptional": true + }, + { + "filePath": "../../node_modules/@shopify/shopify-api/lib/session/types.d.ts", + "syntaxKind": "PropertySignature", + "name": "expires", + "value": "Date", + "description": "", + "isOptional": true + }, + { + "filePath": "../../node_modules/@shopify/shopify-api/lib/session/types.d.ts", + "syntaxKind": "PropertySignature", + "name": "accessToken", + "value": "string", + "description": "", + "isOptional": true + }, + { + "filePath": "../../node_modules/@shopify/shopify-api/lib/session/types.d.ts", + "syntaxKind": "PropertySignature", + "name": "onlineAccessInfo", + "value": "OnlineAccessInfo", + "description": "", + "isOptional": true + } + ], + "value": "export interface SessionParams {\n readonly id: string;\n shop: string;\n state: string;\n isOnline: boolean;\n scope?: string;\n expires?: Date;\n accessToken?: string;\n onlineAccessInfo?: OnlineAccessInfo;\n}" + }, "AdminApiContext": { - "filePath": "/server/clients/admin/types.ts", + "filePath": "src/server/clients/admin/types.ts", "name": "AdminApiContext", "description": "", "members": [ { - "filePath": "/server/clients/admin/types.ts", + "filePath": "src/server/clients/admin/types.ts", "syntaxKind": "PropertySignature", "name": "rest", "value": "RestClientWithResources", @@ -293,7 +564,7 @@ ] }, { - "filePath": "/server/clients/admin/types.ts", + "filePath": "src/server/clients/admin/types.ts", "syntaxKind": "PropertySignature", "name": "graphql", "value": "GraphQLClient", @@ -315,82 +586,328 @@ "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 graphql: GraphQLClient;\n}" }, "RestClientWithResources": { - "filePath": "/server/clients/admin/rest.ts", + "filePath": "src/server/clients/admin/rest.ts", "syntaxKind": "TypeAliasDeclaration", "name": "RestClientWithResources", "value": "RemixRestClient & {resources: Resources}", "description": "" }, - "BillingContext": { - "filePath": "/server/authenticate/admin/billing/types.ts", - "name": "BillingContext", + "RemixRestClient": { + "filePath": "src/server/clients/admin/rest.ts", + "name": "RemixRestClient", + "description": "", + "members": [ + { + "filePath": "src/server/clients/admin/rest.ts", + "syntaxKind": "PropertyDeclaration", + "name": "session", + "value": "Session", + "description": "" + }, + { + "filePath": "src/server/clients/admin/rest.ts", + "syntaxKind": "MethodDeclaration", + "name": "get", + "value": "(params: GetRequestParams) => Promise", + "description": "Performs a GET request on the given path." + }, + { + "filePath": "src/server/clients/admin/rest.ts", + "syntaxKind": "MethodDeclaration", + "name": "post", + "value": "(params: PostRequestParams) => Promise", + "description": "Performs a POST request on the given path." + }, + { + "filePath": "src/server/clients/admin/rest.ts", + "syntaxKind": "MethodDeclaration", + "name": "put", + "value": "(params: PostRequestParams) => Promise", + "description": "Performs a PUT request on the given path." + }, + { + "filePath": "src/server/clients/admin/rest.ts", + "syntaxKind": "MethodDeclaration", + "name": "delete", + "value": "(params: GetRequestParams) => Promise", + "description": "Performs a DELETE request on the given path." + } + ], + "value": "class RemixRestClient {\n public session: Session;\n private params: AdminClientOptions['params'];\n private handleClientError: AdminClientOptions['handleClientError'];\n\n constructor({params, session, handleClientError}: AdminClientOptions) {\n this.params = params;\n this.handleClientError = handleClientError;\n this.session = session;\n }\n\n /**\n * Performs a GET request on the given path.\n */\n public async get(params: GetRequestParams) {\n return this.makeRequest({\n method: 'GET' as RequestParams['method'],\n ...params,\n });\n }\n\n /**\n * Performs a POST request on the given path.\n */\n public async post(params: PostRequestParams) {\n return this.makeRequest({\n method: 'POST' as RequestParams['method'],\n ...params,\n });\n }\n\n /**\n * Performs a PUT request on the given path.\n */\n public async put(params: PutRequestParams) {\n return this.makeRequest({\n method: 'PUT' as RequestParams['method'],\n ...params,\n });\n }\n\n /**\n * Performs a DELETE request on the given path.\n */\n public async delete(params: DeleteRequestParams) {\n return this.makeRequest({\n method: 'DELETE' as RequestParams['method'],\n ...params,\n });\n }\n\n protected async makeRequest(params: RequestParams): Promise {\n const originalClient = new this.params.api.clients.Rest({\n session: this.session,\n });\n const originalRequest = Reflect.get(originalClient, 'request');\n\n try {\n const apiResponse = await originalRequest.call(originalClient, params);\n\n // We use a separate client for REST requests and REST resources because we want to override the API library\n // client class to return a Response object instead.\n return new Response(JSON.stringify(apiResponse.body), {\n headers: apiResponse.headers,\n });\n } catch (error) {\n if (this.handleClientError) {\n throw await this.handleClientError({\n error,\n session: this.session,\n params: this.params,\n });\n } else throw new Error(error);\n }\n }\n}" + }, + "GetRequestParams": { + "filePath": "../../node_modules/@shopify/shopify-api/lib/clients/types.d.ts", + "name": "GetRequestParams", "description": "", "members": [ { - "filePath": "/server/authenticate/admin/billing/types.ts", + "filePath": "../../node_modules/@shopify/shopify-api/lib/clients/types.d.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.", - "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": "Using 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" - } - ] - } - ] + "name": "path", + "value": "string", + "description": "" }, { - "filePath": "/server/authenticate/admin/billing/types.ts", + "filePath": "../../node_modules/@shopify/shopify-api/lib/clients/types.d.ts", "syntaxKind": "PropertySignature", - "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" - } - ] - } - ] + "name": "type", + "value": "DataType", + "description": "", + "isOptional": true }, { - "filePath": "/server/authenticate/admin/billing/types.ts", + "filePath": "../../node_modules/@shopify/shopify-api/lib/clients/types.d.ts", "syntaxKind": "PropertySignature", - "name": "cancel", - "value": "(options: CancelBillingOptions) => Promise", + "name": "data", + "value": "string | Record", + "description": "", + "isOptional": true + }, + { + "filePath": "../../node_modules/@shopify/shopify-api/lib/clients/types.d.ts", + "syntaxKind": "PropertySignature", + "name": "query", + "value": "SearchParams", + "description": "", + "isOptional": true + }, + { + "filePath": "../../node_modules/@shopify/shopify-api/lib/clients/types.d.ts", + "syntaxKind": "PropertySignature", + "name": "extraHeaders", + "value": "HeaderParams", + "description": "", + "isOptional": true + }, + { + "filePath": "../../node_modules/@shopify/shopify-api/lib/clients/types.d.ts", + "syntaxKind": "PropertySignature", + "name": "tries", + "value": "number", + "description": "", + "isOptional": true + } + ], + "value": "export interface GetRequestParams {\n path: string;\n type?: DataType;\n data?: Record | string;\n query?: SearchParams;\n extraHeaders?: HeaderParams;\n tries?: number;\n}" + }, + "DataType": { + "filePath": "../../node_modules/@shopify/shopify-api/lib/clients/types.d.ts", + "syntaxKind": "EnumDeclaration", + "name": "DataType", + "value": "export declare enum DataType {\n JSON = \"application/json\",\n GraphQL = \"application/graphql\",\n URLEncoded = \"application/x-www-form-urlencoded\"\n}", + "members": [ + { + "filePath": "../../node_modules/@shopify/shopify-api/lib/clients/types.d.ts", + "name": "JSON", + "value": "application/json" + }, + { + "filePath": "../../node_modules/@shopify/shopify-api/lib/clients/types.d.ts", + "name": "GraphQL", + "value": "application/graphql" + }, + { + "filePath": "../../node_modules/@shopify/shopify-api/lib/clients/types.d.ts", + "name": "URLEncoded", + "value": "application/x-www-form-urlencoded" + } + ] + }, + "HeaderParams": { + "filePath": "../../node_modules/@shopify/shopify-api/lib/clients/types.d.ts", + "syntaxKind": "TypeAliasDeclaration", + "name": "HeaderParams", + "value": "Record", + "description": "", + "members": [] + }, + "PostRequestParams": { + "filePath": "../../node_modules/@shopify/shopify-api/lib/clients/types.d.ts", + "syntaxKind": "TypeAliasDeclaration", + "name": "PostRequestParams", + "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<\n ResponseWithType>>\n>;" + }, + "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": "", + "isOptional": true + }, + { + "filePath": "src/server/clients/types.ts", + "syntaxKind": "PropertySignature", + "name": "apiVersion", + "value": "ApiVersion", + "description": "", + "isOptional": true + }, + { + "filePath": "src/server/clients/types.ts", + "syntaxKind": "PropertySignature", + "name": "headers", + "value": "{ [key: string]: any; }", + "description": "", + "isOptional": true + }, + { + "filePath": "src/server/clients/types.ts", + "syntaxKind": "PropertySignature", + "name": "tries", + "value": "number", + "description": "", + "isOptional": true + } + ], + "value": "export interface GraphQLQueryOptions<\n Operation extends keyof Operations,\n Operations extends AllOperations,\n> {\n variables?: ApiClientRequestOptions['variables'];\n apiVersion?: ApiVersion;\n headers?: {[key: string]: any};\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" + } + ] + }, + "BillingContext": { + "filePath": "src/server/authenticate/admin/billing/types.ts", + "name": "BillingContext", + "description": "", + "members": [ + { + "filePath": "src/server/authenticate/admin/billing/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.", + "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": "Using 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" + } + ] + } + ] + }, + { + "filePath": "src/server/authenticate/admin/billing/types.ts", + "syntaxKind": "PropertySignature", + "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": "src/server/authenticate/admin/billing/types.ts", + "syntaxKind": "PropertySignature", + "name": "cancel", + "value": "(options: CancelBillingOptions) => Promise", "description": "Cancels an ongoing subscription, given its ID.", "examples": [ { @@ -413,26 +930,26 @@ "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 * Using 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 */\n require: (\n options: RequireBillingOptions,\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": "/server/authenticate/admin/billing/types.ts", + "filePath": "src/server/authenticate/admin/billing/types.ts", "name": "RequireBillingOptions", "description": "", "members": [ { - "filePath": "/server/authenticate/admin/billing/types.ts", + "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": "/server/authenticate/admin/billing/types.ts", + "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": "/server/authenticate/admin/billing/types.ts", + "filePath": "src/server/authenticate/admin/billing/types.ts", "syntaxKind": "PropertySignature", "name": "isTest", "value": "boolean", @@ -442,20 +959,114 @@ ], "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}" }, + "BillingCheckResponseObject": { + "filePath": "../../node_modules/@shopify/shopify-api/lib/billing/types.d.ts", + "name": "BillingCheckResponseObject", + "description": "", + "members": [ + { + "filePath": "../../node_modules/@shopify/shopify-api/lib/billing/types.d.ts", + "syntaxKind": "PropertySignature", + "name": "hasActivePayment", + "value": "boolean", + "description": "" + }, + { + "filePath": "../../node_modules/@shopify/shopify-api/lib/billing/types.d.ts", + "syntaxKind": "PropertySignature", + "name": "oneTimePurchases", + "value": "OneTimePurchase[]", + "description": "" + }, + { + "filePath": "../../node_modules/@shopify/shopify-api/lib/billing/types.d.ts", + "syntaxKind": "PropertySignature", + "name": "appSubscriptions", + "value": "AppSubscription[]", + "description": "" + } + ], + "value": "export interface BillingCheckResponseObject {\n hasActivePayment: boolean;\n oneTimePurchases: OneTimePurchase[];\n appSubscriptions: AppSubscription[];\n}" + }, + "OneTimePurchase": { + "filePath": "../../node_modules/@shopify/shopify-api/lib/billing/types.d.ts", + "name": "OneTimePurchase", + "description": "", + "members": [ + { + "filePath": "../../node_modules/@shopify/shopify-api/lib/billing/types.d.ts", + "syntaxKind": "PropertySignature", + "name": "id", + "value": "string", + "description": "" + }, + { + "filePath": "../../node_modules/@shopify/shopify-api/lib/billing/types.d.ts", + "syntaxKind": "PropertySignature", + "name": "name", + "value": "string", + "description": "" + }, + { + "filePath": "../../node_modules/@shopify/shopify-api/lib/billing/types.d.ts", + "syntaxKind": "PropertySignature", + "name": "test", + "value": "boolean", + "description": "" + }, + { + "filePath": "../../node_modules/@shopify/shopify-api/lib/billing/types.d.ts", + "syntaxKind": "PropertySignature", + "name": "status", + "value": "string", + "description": "" + } + ], + "value": "export interface OneTimePurchase {\n id: string;\n name: string;\n test: boolean;\n status: string;\n}" + }, + "AppSubscription": { + "filePath": "../../node_modules/@shopify/shopify-api/lib/billing/types.d.ts", + "name": "AppSubscription", + "description": "", + "members": [ + { + "filePath": "../../node_modules/@shopify/shopify-api/lib/billing/types.d.ts", + "syntaxKind": "PropertySignature", + "name": "id", + "value": "string", + "description": "" + }, + { + "filePath": "../../node_modules/@shopify/shopify-api/lib/billing/types.d.ts", + "syntaxKind": "PropertySignature", + "name": "name", + "value": "string", + "description": "" + }, + { + "filePath": "../../node_modules/@shopify/shopify-api/lib/billing/types.d.ts", + "syntaxKind": "PropertySignature", + "name": "test", + "value": "boolean", + "description": "" + } + ], + "value": "export interface AppSubscription {\n id: string;\n name: string;\n test: boolean;\n}" + }, "RequestBillingOptions": { - "filePath": "/server/authenticate/admin/billing/types.ts", + "filePath": "src/server/authenticate/admin/billing/types.ts", "name": "RequestBillingOptions", "description": "", "members": [ { - "filePath": "/server/authenticate/admin/billing/types.ts", + "filePath": "src/server/authenticate/admin/billing/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." }, { - "filePath": "/server/authenticate/admin/billing/types.ts", + "filePath": "src/server/authenticate/admin/billing/types.ts", "syntaxKind": "PropertySignature", "name": "isTest", "value": "boolean", @@ -463,7 +1074,7 @@ "isOptional": true }, { - "filePath": "/server/authenticate/admin/billing/types.ts", + "filePath": "src/server/authenticate/admin/billing/types.ts", "syntaxKind": "PropertySignature", "name": "returnUrl", "value": "string", @@ -474,19 +1085,19 @@ "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": "/server/authenticate/admin/billing/types.ts", + "filePath": "src/server/authenticate/admin/billing/types.ts", "name": "CancelBillingOptions", "description": "", "members": [ { - "filePath": "/server/authenticate/admin/billing/types.ts", + "filePath": "src/server/authenticate/admin/billing/types.ts", "syntaxKind": "PropertySignature", "name": "subscriptionId", "value": "string", "description": "The ID of the subscription to cancel." }, { - "filePath": "/server/authenticate/admin/billing/types.ts", + "filePath": "src/server/authenticate/admin/billing/types.ts", "syntaxKind": "PropertySignature", "name": "prorate", "value": "boolean", @@ -494,7 +1105,7 @@ "isOptional": true }, { - "filePath": "/server/authenticate/admin/billing/types.ts", + "filePath": "src/server/authenticate/admin/billing/types.ts", "syntaxKind": "PropertySignature", "name": "isTest", "value": "boolean", @@ -504,13 +1115,20 @@ ], "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": "/server/authenticate/admin/types.ts", + "filePath": "src/server/authenticate/admin/types.ts", "name": "EmbeddedAdminContext", "description": "", "members": [ { - "filePath": "/server/authenticate/admin/types.ts", + "filePath": "src/server/authenticate/admin/types.ts", "syntaxKind": "PropertySignature", "name": "sessionToken", "value": "JwtPayload", @@ -533,7 +1151,7 @@ ] }, { - "filePath": "/server/authenticate/admin/types.ts", + "filePath": "src/server/authenticate/admin/types.ts", "syntaxKind": "PropertySignature", "name": "redirect", "value": "RedirectFunction", @@ -562,7 +1180,7 @@ ] }, { - "filePath": "/server/authenticate/admin/types.ts", + "filePath": "src/server/authenticate/admin/types.ts", "syntaxKind": "PropertySignature", "name": "session", "value": "Session", @@ -599,21 +1217,21 @@ ] }, { - "filePath": "/server/authenticate/admin/types.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": "/server/authenticate/admin/types.ts", + "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": "/server/authenticate/admin/types.ts", + "filePath": "src/server/authenticate/admin/types.ts", "syntaxKind": "PropertySignature", "name": "cors", "value": "EnsureCORSFunction", @@ -634,44 +1252,115 @@ ], "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}" }, - "RedirectFunction": { - "filePath": "/server/authenticate/admin/helpers/redirect.ts", - "name": "RedirectFunction", + "JwtPayload": { + "filePath": "../../node_modules/@shopify/shopify-api/lib/session/types.d.ts", + "name": "JwtPayload", "description": "", - "params": [ + "members": [ { - "name": "url", - "description": "", + "filePath": "../../node_modules/@shopify/shopify-api/lib/session/types.d.ts", + "syntaxKind": "PropertySignature", + "name": "iss", "value": "string", - "filePath": "/server/authenticate/admin/helpers/redirect.ts" + "description": "" }, { - "name": "init", - "description": "", - "value": "RedirectInit", - "isOptional": true, - "filePath": "/server/authenticate/admin/helpers/redirect.ts" - } - ], - "returns": { - "filePath": "/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": "/server/authenticate/admin/helpers/redirect.ts", - "syntaxKind": "TypeAliasDeclaration", - "name": "RedirectInit", - "value": "number | (ResponseInit & {target?: RedirectTarget})", - "description": "" - }, - "RedirectTarget": { - "filePath": "/server/authenticate/admin/helpers/redirect.ts", - "syntaxKind": "TypeAliasDeclaration", - "name": "RedirectTarget", + "filePath": "../../node_modules/@shopify/shopify-api/lib/session/types.d.ts", + "syntaxKind": "PropertySignature", + "name": "dest", + "value": "string", + "description": "" + }, + { + "filePath": "../../node_modules/@shopify/shopify-api/lib/session/types.d.ts", + "syntaxKind": "PropertySignature", + "name": "aud", + "value": "string", + "description": "" + }, + { + "filePath": "../../node_modules/@shopify/shopify-api/lib/session/types.d.ts", + "syntaxKind": "PropertySignature", + "name": "sub", + "value": "string", + "description": "" + }, + { + "filePath": "../../node_modules/@shopify/shopify-api/lib/session/types.d.ts", + "syntaxKind": "PropertySignature", + "name": "exp", + "value": "number", + "description": "" + }, + { + "filePath": "../../node_modules/@shopify/shopify-api/lib/session/types.d.ts", + "syntaxKind": "PropertySignature", + "name": "nbf", + "value": "number", + "description": "" + }, + { + "filePath": "../../node_modules/@shopify/shopify-api/lib/session/types.d.ts", + "syntaxKind": "PropertySignature", + "name": "iat", + "value": "number", + "description": "" + }, + { + "filePath": "../../node_modules/@shopify/shopify-api/lib/session/types.d.ts", + "syntaxKind": "PropertySignature", + "name": "jti", + "value": "string", + "description": "" + }, + { + "filePath": "../../node_modules/@shopify/shopify-api/lib/session/types.d.ts", + "syntaxKind": "PropertySignature", + "name": "sid", + "value": "string", + "description": "" + } + ], + "value": "export interface JwtPayload {\n iss: string;\n dest: string;\n aud: string;\n sub: string;\n exp: number;\n nbf: number;\n iat: number;\n jti: string;\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": "" } @@ -991,12 +1680,12 @@ "type": "BillingContext", "typeDefinitions": { "BillingContext": { - "filePath": "/server/authenticate/admin/billing/types.ts", + "filePath": "src/server/authenticate/admin/billing/types.ts", "name": "BillingContext", "description": "", "members": [ { - "filePath": "/server/authenticate/admin/billing/types.ts", + "filePath": "src/server/authenticate/admin/billing/types.ts", "syntaxKind": "PropertySignature", "name": "require", "value": "(options: RequireBillingOptions) => Promise", @@ -1033,7 +1722,7 @@ ] }, { - "filePath": "/server/authenticate/admin/billing/types.ts", + "filePath": "src/server/authenticate/admin/billing/types.ts", "syntaxKind": "PropertySignature", "name": "request", "value": "(options: RequestBillingOptions) => Promise", @@ -1056,7 +1745,7 @@ ] }, { - "filePath": "/server/authenticate/admin/billing/types.ts", + "filePath": "src/server/authenticate/admin/billing/types.ts", "syntaxKind": "PropertySignature", "name": "cancel", "value": "(options: CancelBillingOptions) => Promise", @@ -1082,26 +1771,26 @@ "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 * Using 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 */\n require: (\n options: RequireBillingOptions,\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": "/server/authenticate/admin/billing/types.ts", + "filePath": "src/server/authenticate/admin/billing/types.ts", "name": "RequireBillingOptions", "description": "", "members": [ { - "filePath": "/server/authenticate/admin/billing/types.ts", + "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": "/server/authenticate/admin/billing/types.ts", + "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": "/server/authenticate/admin/billing/types.ts", + "filePath": "src/server/authenticate/admin/billing/types.ts", "syntaxKind": "PropertySignature", "name": "isTest", "value": "boolean", @@ -1111,20 +1800,114 @@ ], "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}" }, + "BillingCheckResponseObject": { + "filePath": "../../node_modules/@shopify/shopify-api/lib/billing/types.d.ts", + "name": "BillingCheckResponseObject", + "description": "", + "members": [ + { + "filePath": "../../node_modules/@shopify/shopify-api/lib/billing/types.d.ts", + "syntaxKind": "PropertySignature", + "name": "hasActivePayment", + "value": "boolean", + "description": "" + }, + { + "filePath": "../../node_modules/@shopify/shopify-api/lib/billing/types.d.ts", + "syntaxKind": "PropertySignature", + "name": "oneTimePurchases", + "value": "OneTimePurchase[]", + "description": "" + }, + { + "filePath": "../../node_modules/@shopify/shopify-api/lib/billing/types.d.ts", + "syntaxKind": "PropertySignature", + "name": "appSubscriptions", + "value": "AppSubscription[]", + "description": "" + } + ], + "value": "export interface BillingCheckResponseObject {\n hasActivePayment: boolean;\n oneTimePurchases: OneTimePurchase[];\n appSubscriptions: AppSubscription[];\n}" + }, + "OneTimePurchase": { + "filePath": "../../node_modules/@shopify/shopify-api/lib/billing/types.d.ts", + "name": "OneTimePurchase", + "description": "", + "members": [ + { + "filePath": "../../node_modules/@shopify/shopify-api/lib/billing/types.d.ts", + "syntaxKind": "PropertySignature", + "name": "id", + "value": "string", + "description": "" + }, + { + "filePath": "../../node_modules/@shopify/shopify-api/lib/billing/types.d.ts", + "syntaxKind": "PropertySignature", + "name": "name", + "value": "string", + "description": "" + }, + { + "filePath": "../../node_modules/@shopify/shopify-api/lib/billing/types.d.ts", + "syntaxKind": "PropertySignature", + "name": "test", + "value": "boolean", + "description": "" + }, + { + "filePath": "../../node_modules/@shopify/shopify-api/lib/billing/types.d.ts", + "syntaxKind": "PropertySignature", + "name": "status", + "value": "string", + "description": "" + } + ], + "value": "export interface OneTimePurchase {\n id: string;\n name: string;\n test: boolean;\n status: string;\n}" + }, + "AppSubscription": { + "filePath": "../../node_modules/@shopify/shopify-api/lib/billing/types.d.ts", + "name": "AppSubscription", + "description": "", + "members": [ + { + "filePath": "../../node_modules/@shopify/shopify-api/lib/billing/types.d.ts", + "syntaxKind": "PropertySignature", + "name": "id", + "value": "string", + "description": "" + }, + { + "filePath": "../../node_modules/@shopify/shopify-api/lib/billing/types.d.ts", + "syntaxKind": "PropertySignature", + "name": "name", + "value": "string", + "description": "" + }, + { + "filePath": "../../node_modules/@shopify/shopify-api/lib/billing/types.d.ts", + "syntaxKind": "PropertySignature", + "name": "test", + "value": "boolean", + "description": "" + } + ], + "value": "export interface AppSubscription {\n id: string;\n name: string;\n test: boolean;\n}" + }, "RequestBillingOptions": { - "filePath": "/server/authenticate/admin/billing/types.ts", + "filePath": "src/server/authenticate/admin/billing/types.ts", "name": "RequestBillingOptions", "description": "", "members": [ { - "filePath": "/server/authenticate/admin/billing/types.ts", + "filePath": "src/server/authenticate/admin/billing/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." }, { - "filePath": "/server/authenticate/admin/billing/types.ts", + "filePath": "src/server/authenticate/admin/billing/types.ts", "syntaxKind": "PropertySignature", "name": "isTest", "value": "boolean", @@ -1132,7 +1915,7 @@ "isOptional": true }, { - "filePath": "/server/authenticate/admin/billing/types.ts", + "filePath": "src/server/authenticate/admin/billing/types.ts", "syntaxKind": "PropertySignature", "name": "returnUrl", "value": "string", @@ -1143,19 +1926,19 @@ "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": "/server/authenticate/admin/billing/types.ts", + "filePath": "src/server/authenticate/admin/billing/types.ts", "name": "CancelBillingOptions", "description": "", "members": [ { - "filePath": "/server/authenticate/admin/billing/types.ts", + "filePath": "src/server/authenticate/admin/billing/types.ts", "syntaxKind": "PropertySignature", "name": "subscriptionId", "value": "string", "description": "The ID of the subscription to cancel." }, { - "filePath": "/server/authenticate/admin/billing/types.ts", + "filePath": "src/server/authenticate/admin/billing/types.ts", "syntaxKind": "PropertySignature", "name": "prorate", "value": "boolean", @@ -1163,7 +1946,7 @@ "isOptional": true }, { - "filePath": "/server/authenticate/admin/billing/types.ts", + "filePath": "src/server/authenticate/admin/billing/types.ts", "syntaxKind": "PropertySignature", "name": "isTest", "value": "boolean", @@ -1293,7 +2076,7 @@ "type": "AuthenticateAppProxy", "typeDefinitions": { "AuthenticateAppProxy": { - "filePath": "/server/authenticate/public/appProxy/types.ts", + "filePath": "src/server/authenticate/public/appProxy/types.ts", "name": "AuthenticateAppProxy", "description": "", "params": [ @@ -1301,11 +2084,11 @@ "name": "request", "description": "", "value": "Request", - "filePath": "/server/authenticate/public/appProxy/types.ts" + "filePath": "src/server/authenticate/public/appProxy/types.ts" } ], "returns": { - "filePath": "/server/authenticate/public/appProxy/types.ts", + "filePath": "src/server/authenticate/public/appProxy/types.ts", "description": "", "name": "Promise", "value": "Promise" @@ -1313,33 +2096,33 @@ "value": "export type AuthenticateAppProxy = (\n request: Request,\n) => Promise;" }, "AppProxyContext": { - "filePath": "/server/authenticate/public/appProxy/types.ts", + "filePath": "src/server/authenticate/public/appProxy/types.ts", "name": "AppProxyContext", "description": "", "members": [ { - "filePath": "/server/authenticate/public/appProxy/types.ts", + "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": "/server/authenticate/public/appProxy/types.ts", + "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": "/server/authenticate/public/appProxy/types.ts", + "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": "/server/authenticate/public/appProxy/types.ts", + "filePath": "src/server/authenticate/public/appProxy/types.ts", "syntaxKind": "PropertySignature", "name": "liquid", "value": "LiquidResponseFunction", @@ -1361,7 +2144,7 @@ "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": "/server/authenticate/public/appProxy/types.ts", + "filePath": "src/server/authenticate/public/appProxy/types.ts", "name": "LiquidResponseFunction", "description": "", "params": [ @@ -1369,18 +2152,18 @@ "name": "body", "description": "", "value": "string", - "filePath": "/server/authenticate/public/appProxy/types.ts" + "filePath": "src/server/authenticate/public/appProxy/types.ts" }, { "name": "initAndOptions", "description": "", "value": "number | (ResponseInit & Options)", "isOptional": true, - "filePath": "/server/authenticate/public/appProxy/types.ts" + "filePath": "src/server/authenticate/public/appProxy/types.ts" } ], "returns": { - "filePath": "/server/authenticate/public/appProxy/types.ts", + "filePath": "src/server/authenticate/public/appProxy/types.ts", "description": "", "name": "Response", "value": "Response" @@ -1388,12 +2171,12 @@ "value": "export type LiquidResponseFunction = (\n body: string,\n initAndOptions?: number | (ResponseInit & Options),\n) => Response;" }, "Options": { - "filePath": "/server/authenticate/public/appProxy/types.ts", + "filePath": "src/server/authenticate/public/appProxy/types.ts", "name": "Options", "description": "", "members": [ { - "filePath": "/server/authenticate/public/appProxy/types.ts", + "filePath": "src/server/authenticate/public/appProxy/types.ts", "syntaxKind": "PropertySignature", "name": "layout", "value": "boolean", @@ -1404,12 +2187,12 @@ "value": "interface Options {\n layout?: boolean;\n}" }, "AppProxyContextWithSession": { - "filePath": "/server/authenticate/public/appProxy/types.ts", + "filePath": "src/server/authenticate/public/appProxy/types.ts", "name": "AppProxyContextWithSession", "description": "", "members": [ { - "filePath": "/server/authenticate/public/appProxy/types.ts", + "filePath": "src/server/authenticate/public/appProxy/types.ts", "syntaxKind": "PropertySignature", "name": "session", "value": "Session", @@ -1428,7 +2211,7 @@ ] }, { - "filePath": "/server/authenticate/public/appProxy/types.ts", + "filePath": "src/server/authenticate/public/appProxy/types.ts", "syntaxKind": "PropertySignature", "name": "admin", "value": "AdminApiContext", @@ -1447,7 +2230,7 @@ ] }, { - "filePath": "/server/authenticate/public/appProxy/types.ts", + "filePath": "src/server/authenticate/public/appProxy/types.ts", "syntaxKind": "PropertySignature", "name": "storefront", "value": "StorefrontContext", @@ -1466,7 +2249,7 @@ ] }, { - "filePath": "/server/authenticate/public/appProxy/types.ts", + "filePath": "src/server/authenticate/public/appProxy/types.ts", "syntaxKind": "PropertySignature", "name": "liquid", "value": "LiquidResponseFunction", @@ -1487,4115 +2270,11805 @@ ], "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}" }, - "AdminApiContext": { - "filePath": "/server/clients/admin/types.ts", - "name": "AdminApiContext", - "description": "", + "Session": { + "filePath": "../../node_modules/@shopify/shopify-api/lib/session/session.d.ts", + "name": "Session", + "description": "Stores App information from logged in merchants so they can make authenticated requests to the Admin API.", "members": [ { - "filePath": "/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 { admin, session } = await authenticate.admin(request);\n return json(admin.rest.resources.Order.count({ session }));\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};", - "title": "/app/routes/**\\/*.ts" - }, - { - "code": "import { 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" - } - ] - }, - { - "title": "Performing a POST request to the REST API", - "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};", - "title": "/app/routes/**\\/*.ts" - }, - { - "code": "import { 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": "../../node_modules/@shopify/shopify-api/lib/session/session.d.ts", + "syntaxKind": "PropertyDeclaration", + "name": "id", + "value": "string", + "description": "" }, { - "filePath": "/server/clients/admin/types.ts", - "syntaxKind": "PropertySignature", - "name": "graphql", - "value": "GraphQLClient", - "description": "Methods for interacting with the Shopify Admin GraphQL API\n\n\n\n\n\n\n\n\n\n", - "examples": [ - { - "title": "Querying the GraphQL API", - "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" - } - ] - } - ] + "filePath": "../../node_modules/@shopify/shopify-api/lib/session/session.d.ts", + "syntaxKind": "PropertyDeclaration", + "name": "shop", + "value": "string", + "description": "" + }, + { + "filePath": "../../node_modules/@shopify/shopify-api/lib/session/session.d.ts", + "syntaxKind": "PropertyDeclaration", + "name": "state", + "value": "string", + "description": "" + }, + { + "filePath": "../../node_modules/@shopify/shopify-api/lib/session/session.d.ts", + "syntaxKind": "PropertyDeclaration", + "name": "isOnline", + "value": "boolean", + "description": "" + }, + { + "filePath": "../../node_modules/@shopify/shopify-api/lib/session/session.d.ts", + "syntaxKind": "PropertyDeclaration", + "name": "scope", + "value": "string", + "description": "" + }, + { + "filePath": "../../node_modules/@shopify/shopify-api/lib/session/session.d.ts", + "syntaxKind": "PropertyDeclaration", + "name": "expires", + "value": "Date", + "description": "" + }, + { + "filePath": "../../node_modules/@shopify/shopify-api/lib/session/session.d.ts", + "syntaxKind": "PropertyDeclaration", + "name": "accessToken", + "value": "string", + "description": "" + }, + { + "filePath": "../../node_modules/@shopify/shopify-api/lib/session/session.d.ts", + "syntaxKind": "PropertyDeclaration", + "name": "onlineAccessInfo", + "value": "OnlineAccessInfo", + "description": "" + }, + { + "filePath": "../../node_modules/@shopify/shopify-api/lib/session/session.d.ts", + "syntaxKind": "MethodDeclaration", + "name": "isActive", + "value": "(scopes: string | string[] | AuthScopes) => boolean", + "description": "" + }, + { + "filePath": "../../node_modules/@shopify/shopify-api/lib/session/session.d.ts", + "syntaxKind": "MethodDeclaration", + "name": "isScopeChanged", + "value": "(scopes: string | string[] | AuthScopes) => boolean", + "description": "" + }, + { + "filePath": "../../node_modules/@shopify/shopify-api/lib/session/session.d.ts", + "syntaxKind": "MethodDeclaration", + "name": "isExpired", + "value": "(withinMillisecondsOfExpiry?: number) => boolean", + "description": "" + }, + { + "filePath": "../../node_modules/@shopify/shopify-api/lib/session/session.d.ts", + "syntaxKind": "MethodDeclaration", + "name": "toObject", + "value": "() => SessionParams", + "description": "" + }, + { + "filePath": "../../node_modules/@shopify/shopify-api/lib/session/session.d.ts", + "syntaxKind": "MethodDeclaration", + "name": "equals", + "value": "(other: Session) => boolean", + "description": "" + }, + { + "filePath": "../../node_modules/@shopify/shopify-api/lib/session/session.d.ts", + "syntaxKind": "MethodDeclaration", + "name": "toPropertyArray", + "value": "() => [string, string | number | boolean][]", + "description": "" } ], - "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 graphql: GraphQLClient;\n}" - }, - "RestClientWithResources": { - "filePath": "/server/clients/admin/rest.ts", - "syntaxKind": "TypeAliasDeclaration", - "name": "RestClientWithResources", - "value": "RemixRestClient & {resources: Resources}", - "description": "" + "value": "export declare class Session {\n static fromPropertyArray(entries: [string, string | number | boolean][]): Session;\n readonly id: string;\n shop: string;\n state: string;\n isOnline: boolean;\n scope?: string;\n expires?: Date;\n accessToken?: string;\n onlineAccessInfo?: OnlineAccessInfo;\n constructor(params: SessionParams);\n isActive(scopes: AuthScopes | string | string[]): boolean;\n isScopeChanged(scopes: AuthScopes | string | string[]): boolean;\n isExpired(withinMillisecondsOfExpiry?: number): boolean;\n toObject(): SessionParams;\n equals(other: Session | undefined): boolean;\n toPropertyArray(): [string, string | number | boolean][];\n}" }, - "StorefrontContext": { - "filePath": "/server/clients/storefront/types.ts", - "name": "StorefrontContext", + "OnlineAccessInfo": { + "filePath": "../../node_modules/@shopify/shopify-api/lib/auth/oauth/types.d.ts", + "name": "OnlineAccessInfo", "description": "", "members": [ { - "filePath": "/server/clients/storefront/types.ts", + "filePath": "../../node_modules/@shopify/shopify-api/lib/auth/oauth/types.d.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" - } - ] - } - ] + "name": "expires_in", + "value": "number", + "description": "" + }, + { + "filePath": "../../node_modules/@shopify/shopify-api/lib/auth/oauth/types.d.ts", + "syntaxKind": "PropertySignature", + "name": "associated_user_scope", + "value": "string", + "description": "" + }, + { + "filePath": "../../node_modules/@shopify/shopify-api/lib/auth/oauth/types.d.ts", + "syntaxKind": "PropertySignature", + "name": "associated_user", + "value": "{ id: number; first_name: string; last_name: string; email: string; email_verified: boolean; account_owner: boolean; locale: string; collaborator: boolean; }", + "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 graphql: GraphQLClient;\n}" - } - } - } - ], - "jsDocTypeExamples": [ - "AppProxyContextWithSession" - ], - "related": [ - { - "name": "Admin API context", - "subtitle": "Interact with the Admin API.", - "url": "/docs/api/shopify-app-remix/apis/admin-api" - }, - { - "name": "Storefront API context", - "subtitle": "Interact with the Storefront API.", - "url": "/docs/api/shopify-app-remix/apis/storefront-api" - } - ], - "examples": { - "description": "", - "exampleGroups": [ - { - "title": "session", - "examples": [ - { - "description": "Get the session for the shop that initiated the request to the app proxy.", - "codeblock": { - "title": "Using the session object", - "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};", - "language": "typescript" - } - ] - } - } - ] - }, - { - "title": "admin", - "examples": [ - { - "description": "Use the `admin` object to interact with the REST or GraphQL APIs.", - "codeblock": { - "title": "Interacting with the Admin API", - "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}", - "language": "typescript" - } - ] - } - } - ] - }, - { - "title": "storefront", - "examples": [ - { - "description": "Use the `storefront` object to interact with the GraphQL API.", - "codeblock": { - "title": "Interacting with the Storefront API", - "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}", - "language": "typescript" - } - ] - } - } - ] - }, - { - "title": "liquid", - "examples": [ - { - "description": "Use the `liquid` helper to render a `Response` with Liquid content.", - "codeblock": { - "title": "Rendering liquid content", - "tabs": [ - { - "title": "app/routes/**\\/.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}", - "language": "typescript" - } - ] - } - } - ] - } - ] - } - }, - { - "name": "Checkout", - "description": "The `authenticate.public.checkout` function ensures that checkout extension requests are coming from Shopify, and returns helpers to respond with the correct headers.", - "category": "Authenticate", - "subCategory": "Public", - "type": "object", - "isVisualComponent": false, - "definitions": [ - { - "title": "authenticate.public.checkout", - "description": "Authenticates requests coming from Shopify checkout extensions.", - "type": "AuthenticateCheckout", - "typeDefinitions": { - "AuthenticateCheckout": { - "filePath": "/server/authenticate/public/checkout/types.ts", - "name": "AuthenticateCheckout", + "value": "export interface OnlineAccessInfo {\n expires_in: number;\n associated_user_scope: string;\n associated_user: {\n id: number;\n first_name: string;\n last_name: string;\n email: string;\n email_verified: boolean;\n account_owner: boolean;\n locale: string;\n collaborator: boolean;\n };\n}" + }, + "AuthScopes": { + "filePath": "../../node_modules/@shopify/shopify-api/lib/auth/scopes/index.d.ts", + "name": "AuthScopes", "description": "", - "params": [ + "members": [ { - "name": "request", - "description": "", - "value": "Request", - "filePath": "/server/authenticate/public/checkout/types.ts" + "filePath": "../../node_modules/@shopify/shopify-api/lib/auth/scopes/index.d.ts", + "syntaxKind": "MethodDeclaration", + "name": "has", + "value": "(scope: string | string[] | AuthScopes) => boolean", + "description": "" }, { - "name": "options", - "description": "", - "value": "AuthenticateCheckoutOptions", - "isOptional": true, - "filePath": "/server/authenticate/public/checkout/types.ts" + "filePath": "../../node_modules/@shopify/shopify-api/lib/auth/scopes/index.d.ts", + "syntaxKind": "MethodDeclaration", + "name": "equals", + "value": "(otherScopes: string | string[] | AuthScopes) => boolean", + "description": "" + }, + { + "filePath": "../../node_modules/@shopify/shopify-api/lib/auth/scopes/index.d.ts", + "syntaxKind": "MethodDeclaration", + "name": "toString", + "value": "() => string", + "description": "" + }, + { + "filePath": "../../node_modules/@shopify/shopify-api/lib/auth/scopes/index.d.ts", + "syntaxKind": "MethodDeclaration", + "name": "toArray", + "value": "() => string[]", + "description": "" } ], - "returns": { - "filePath": "/server/authenticate/public/checkout/types.ts", - "description": "", - "name": "Promise", - "value": "Promise" - }, - "value": "export type AuthenticateCheckout = (\n request: Request,\n options?: AuthenticateCheckoutOptions,\n) => Promise;" + "value": "declare class AuthScopes {\n static SCOPE_DELIMITER: string;\n private compressedScopes;\n private expandedScopes;\n constructor(scopes: string | string[] | AuthScopes | undefined);\n has(scope: string | string[] | AuthScopes | undefined): boolean;\n equals(otherScopes: string | string[] | AuthScopes | undefined): boolean;\n toString(): string;\n toArray(): string[];\n private getImpliedScopes;\n}" }, - "AuthenticateCheckoutOptions": { - "filePath": "/server/authenticate/public/checkout/types.ts", - "name": "AuthenticateCheckoutOptions", + "SessionParams": { + "filePath": "../../node_modules/@shopify/shopify-api/lib/session/types.d.ts", + "name": "SessionParams", "description": "", "members": [ { - "filePath": "/server/authenticate/public/checkout/types.ts", + "filePath": "../../node_modules/@shopify/shopify-api/lib/session/types.d.ts", "syntaxKind": "PropertySignature", - "name": "corsHeaders", - "value": "string[]", + "name": "id", + "value": "string", + "description": "" + }, + { + "filePath": "../../node_modules/@shopify/shopify-api/lib/session/types.d.ts", + "syntaxKind": "PropertySignature", + "name": "shop", + "value": "string", + "description": "" + }, + { + "filePath": "../../node_modules/@shopify/shopify-api/lib/session/types.d.ts", + "syntaxKind": "PropertySignature", + "name": "state", + "value": "string", + "description": "" + }, + { + "filePath": "../../node_modules/@shopify/shopify-api/lib/session/types.d.ts", + "syntaxKind": "PropertySignature", + "name": "isOnline", + "value": "boolean", + "description": "" + }, + { + "filePath": "../../node_modules/@shopify/shopify-api/lib/session/types.d.ts", + "syntaxKind": "PropertySignature", + "name": "scope", + "value": "string", + "description": "", + "isOptional": true + }, + { + "filePath": "../../node_modules/@shopify/shopify-api/lib/session/types.d.ts", + "syntaxKind": "PropertySignature", + "name": "expires", + "value": "Date", + "description": "", + "isOptional": true + }, + { + "filePath": "../../node_modules/@shopify/shopify-api/lib/session/types.d.ts", + "syntaxKind": "PropertySignature", + "name": "accessToken", + "value": "string", + "description": "", + "isOptional": true + }, + { + "filePath": "../../node_modules/@shopify/shopify-api/lib/session/types.d.ts", + "syntaxKind": "PropertySignature", + "name": "onlineAccessInfo", + "value": "OnlineAccessInfo", "description": "", "isOptional": true } ], - "value": "export interface AuthenticateCheckoutOptions {\n corsHeaders?: string[];\n}" + "value": "export interface SessionParams {\n readonly id: string;\n shop: string;\n state: string;\n isOnline: boolean;\n scope?: string;\n expires?: Date;\n accessToken?: string;\n onlineAccessInfo?: OnlineAccessInfo;\n}" }, - "CheckoutContext": { - "filePath": "/server/authenticate/public/checkout/types.ts", - "name": "CheckoutContext", - "description": "Authenticated Context for a checkout request", + "AdminApiContext": { + "filePath": "src/server/clients/admin/types.ts", + "name": "AdminApiContext", + "description": "", "members": [ { - "filePath": "/server/authenticate/public/checkout/types.ts", + "filePath": "src/server/clients/admin/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).", + "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 the decoded session token", - "description": "Get store-specific data using the `sessionToken` object.", + "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\";\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" + "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 { 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" } ] - } - ] - }, - { - "filePath": "/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.", + "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\";\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" + "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};", + "title": "/app/routes/**\\/*.ts" + }, + { + "code": "import { 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" } ] - } - ] - } - ], - "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}" - } - } - } - ], - "jsDocTypeExamples": [ - "CheckoutContext" - ], - "related": [], - "examples": { - "description": "", - "exampleGroups": [ - { - "title": "sessionToken", - "examples": [ - { - "description": "Get store-specific data using the `sessionToken` object.", - "codeblock": { - "title": "Using the decoded session token", - "tabs": [ + }, { - "title": "app/routes/public/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 } = await authenticate.public.checkout(\n request\n );\n return json(await getMyAppData({shop: sessionToken.dest}));\n};", - "language": "typescript" + "title": "Performing a POST request to the REST API", + "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};", + "title": "/app/routes/**\\/*.ts" + }, + { + "code": "import { 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" + } + ] } ] - } - } - ] - }, - { - "title": "cors", - "examples": [ - { - "description": "Use the `cors` helper to ensure your app can respond to checkout extension requests.", - "codeblock": { - "title": "Setting CORS headers for a public request", - "tabs": [ + }, + { + "filePath": "src/server/clients/admin/types.ts", + "syntaxKind": "PropertySignature", + "name": "graphql", + "value": "GraphQLClient", + "description": "Methods for interacting with the Shopify Admin GraphQL API\n\n\n\n\n\n\n\n\n\n", + "examples": [ { - "title": "app/routes/public/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};", - "language": "typescript" + "title": "Querying the GraphQL API", + "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" + } + ] } ] } - } - ] - } - ] - } - }, - { - "name": "Webhook", - "description": "Contains functions for verifying Shopify webhooks.\n\n> Note: The format of the `admin` object returned by this function changes with the `v3_webhookAdminContext` future flag. Learn more about [gradual feature adoption](/docs/api/shopify-app-remix/guide-future-flags).", - "category": "Authenticate", - "type": "object", - "isVisualComponent": false, - "definitions": [ - { - "title": "authenticate.webhook", - "description": "Verifies requests coming from Shopify webhooks.", - "type": "AuthenticateWebhook", - "typeDefinitions": { - "AuthenticateWebhook": { - "filePath": "/server/authenticate/webhooks/types.ts", - "name": "AuthenticateWebhook", - "description": "", - "params": [ - { - "name": "request", - "description": "", - "value": "Request", - "filePath": "/server/authenticate/webhooks/types.ts" - } ], - "returns": { - "filePath": "/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>;" + "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 graphql: GraphQLClient;\n}" }, - "WebhookContext": { - "filePath": "/server/authenticate/webhooks/types.ts", + "RestClientWithResources": { + "filePath": "src/server/clients/admin/rest.ts", "syntaxKind": "TypeAliasDeclaration", - "name": "WebhookContext", - "value": "WebhookContextWithoutSession | WebhookContextWithSession", + "name": "RestClientWithResources", + "value": "RemixRestClient & {resources: Resources}", "description": "" }, - "WebhookContextWithoutSession": { - "filePath": "/server/authenticate/webhooks/types.ts", - "name": "WebhookContextWithoutSession", + "RemixRestClient": { + "filePath": "src/server/clients/admin/rest.ts", + "name": "RemixRestClient", "description": "", "members": [ { - "filePath": "/server/authenticate/webhooks/types.ts", - "syntaxKind": "PropertySignature", + "filePath": "src/server/clients/admin/rest.ts", + "syntaxKind": "PropertyDeclaration", "name": "session", - "value": "undefined", + "value": "Session", "description": "" }, { - "filePath": "/server/authenticate/webhooks/types.ts", + "filePath": "src/server/clients/admin/rest.ts", + "syntaxKind": "MethodDeclaration", + "name": "get", + "value": "(params: GetRequestParams) => Promise", + "description": "Performs a GET request on the given path." + }, + { + "filePath": "src/server/clients/admin/rest.ts", + "syntaxKind": "MethodDeclaration", + "name": "post", + "value": "(params: PostRequestParams) => Promise", + "description": "Performs a POST request on the given path." + }, + { + "filePath": "src/server/clients/admin/rest.ts", + "syntaxKind": "MethodDeclaration", + "name": "put", + "value": "(params: PostRequestParams) => Promise", + "description": "Performs a PUT request on the given path." + }, + { + "filePath": "src/server/clients/admin/rest.ts", + "syntaxKind": "MethodDeclaration", + "name": "delete", + "value": "(params: GetRequestParams) => Promise", + "description": "Performs a DELETE request on the given path." + } + ], + "value": "class RemixRestClient {\n public session: Session;\n private params: AdminClientOptions['params'];\n private handleClientError: AdminClientOptions['handleClientError'];\n\n constructor({params, session, handleClientError}: AdminClientOptions) {\n this.params = params;\n this.handleClientError = handleClientError;\n this.session = session;\n }\n\n /**\n * Performs a GET request on the given path.\n */\n public async get(params: GetRequestParams) {\n return this.makeRequest({\n method: 'GET' as RequestParams['method'],\n ...params,\n });\n }\n\n /**\n * Performs a POST request on the given path.\n */\n public async post(params: PostRequestParams) {\n return this.makeRequest({\n method: 'POST' as RequestParams['method'],\n ...params,\n });\n }\n\n /**\n * Performs a PUT request on the given path.\n */\n public async put(params: PutRequestParams) {\n return this.makeRequest({\n method: 'PUT' as RequestParams['method'],\n ...params,\n });\n }\n\n /**\n * Performs a DELETE request on the given path.\n */\n public async delete(params: DeleteRequestParams) {\n return this.makeRequest({\n method: 'DELETE' as RequestParams['method'],\n ...params,\n });\n }\n\n protected async makeRequest(params: RequestParams): Promise {\n const originalClient = new this.params.api.clients.Rest({\n session: this.session,\n });\n const originalRequest = Reflect.get(originalClient, 'request');\n\n try {\n const apiResponse = await originalRequest.call(originalClient, params);\n\n // We use a separate client for REST requests and REST resources because we want to override the API library\n // client class to return a Response object instead.\n return new Response(JSON.stringify(apiResponse.body), {\n headers: apiResponse.headers,\n });\n } catch (error) {\n if (this.handleClientError) {\n throw await this.handleClientError({\n error,\n session: this.session,\n params: this.params,\n });\n } else throw new Error(error);\n }\n }\n}" + }, + "GetRequestParams": { + "filePath": "../../node_modules/@shopify/shopify-api/lib/clients/types.d.ts", + "name": "GetRequestParams", + "description": "", + "members": [ + { + "filePath": "../../node_modules/@shopify/shopify-api/lib/clients/types.d.ts", "syntaxKind": "PropertySignature", - "name": "admin", - "value": "undefined", + "name": "path", + "value": "string", "description": "" }, { - "filePath": "/server/authenticate/webhooks/types.ts", + "filePath": "../../node_modules/@shopify/shopify-api/lib/clients/types.d.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" - } - ] - } - ] + "name": "type", + "value": "DataType", + "description": "", + "isOptional": true }, { - "filePath": "/server/authenticate/webhooks/types.ts", + "filePath": "../../node_modules/@shopify/shopify-api/lib/clients/types.d.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" - } - ] - } - ] + "name": "data", + "value": "string | Record", + "description": "", + "isOptional": true }, { - "filePath": "/server/authenticate/webhooks/types.ts", + "filePath": "../../node_modules/@shopify/shopify-api/lib/clients/types.d.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" - } - ] - } - ] + "name": "query", + "value": "SearchParams", + "description": "", + "isOptional": true }, { - "filePath": "/server/authenticate/webhooks/types.ts", + "filePath": "../../node_modules/@shopify/shopify-api/lib/clients/types.d.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" - } - ] - } - ] + "name": "extraHeaders", + "value": "HeaderParams", + "description": "", + "isOptional": true }, { - "filePath": "/server/authenticate/webhooks/types.ts", + "filePath": "../../node_modules/@shopify/shopify-api/lib/clients/types.d.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" - } - ] - } - ] + "name": "tries", + "value": "number", + "description": "", + "isOptional": true } ], - "value": "export interface WebhookContextWithoutSession\n extends Context {\n session: undefined;\n admin: undefined;\n}" + "value": "export interface GetRequestParams {\n path: string;\n type?: DataType;\n data?: Record | string;\n query?: SearchParams;\n extraHeaders?: HeaderParams;\n tries?: number;\n}" }, - "JSONValue": { - "filePath": "/server/types.ts", + "DataType": { + "filePath": "../../node_modules/@shopify/shopify-api/lib/clients/types.d.ts", + "syntaxKind": "EnumDeclaration", + "name": "DataType", + "value": "export declare enum DataType {\n JSON = \"application/json\",\n GraphQL = \"application/graphql\",\n URLEncoded = \"application/x-www-form-urlencoded\"\n}", + "members": [ + { + "filePath": "../../node_modules/@shopify/shopify-api/lib/clients/types.d.ts", + "name": "JSON", + "value": "application/json" + }, + { + "filePath": "../../node_modules/@shopify/shopify-api/lib/clients/types.d.ts", + "name": "GraphQL", + "value": "application/graphql" + }, + { + "filePath": "../../node_modules/@shopify/shopify-api/lib/clients/types.d.ts", + "name": "URLEncoded", + "value": "application/x-www-form-urlencoded" + } + ] + }, + "HeaderParams": { + "filePath": "../../node_modules/@shopify/shopify-api/lib/clients/types.d.ts", "syntaxKind": "TypeAliasDeclaration", - "name": "JSONValue", - "value": "string | number | boolean | null | JSONObject | JSONArray", + "name": "HeaderParams", + "value": "Record", + "description": "", + "members": [] + }, + "PostRequestParams": { + "filePath": "../../node_modules/@shopify/shopify-api/lib/clients/types.d.ts", + "syntaxKind": "TypeAliasDeclaration", + "name": "PostRequestParams", + "value": "GetRequestParams & {\n data: Record | string;\n}", "description": "" }, - "JSONObject": { - "filePath": "/server/types.ts", - "name": "JSONObject", + "GraphQLClient": { + "filePath": "src/server/clients/types.ts", + "name": "GraphQLClient", "description": "", - "members": [ + "params": [ { - "filePath": "/server/types.ts", - "name": "[x: string]", - "value": "JSONValue" + "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" } ], - "value": "interface JSONObject {\n [x: string]: JSONValue;\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<\n ResponseWithType>>\n>;" }, - "JSONArray": { - "filePath": "/server/types.ts", - "name": "JSONArray", + "GraphQLQueryOptions": { + "filePath": "src/server/clients/types.ts", + "name": "GraphQLQueryOptions", "description": "", "members": [ { - "filePath": "/server/types.ts", + "filePath": "src/server/clients/types.ts", "syntaxKind": "PropertySignature", - "name": "length", - "value": "number", - "description": "Gets or sets the length of the array. This is a number one higher than the highest index in the array." + "name": "variables", + "value": "ApiClientRequestOptions[\"variables\"]", + "description": "", + "isOptional": true }, { - "filePath": "/server/types.ts", - "syntaxKind": "MethodSignature", - "name": "toString", - "value": "() => string", - "description": "Returns a string representation of an array." - }, - { - "filePath": "/server/types.ts", - "syntaxKind": "MethodSignature", - "name": "toLocaleString", - "value": "() => string", - "description": "Returns a string representation of an array. The elements are converted to string using their toLocaleString methods." - }, - { - "filePath": "/server/types.ts", - "syntaxKind": "MethodSignature", - "name": "pop", - "value": "() => JSONValue", - "description": "Removes the last element from an array and returns it.\r\nIf the array is empty, undefined is returned and the array is not modified." - }, - { - "filePath": "/server/types.ts", - "syntaxKind": "MethodSignature", - "name": "push", - "value": "(...items: JSONValue[]) => number", - "description": "Appends new elements to the end of an array, and returns the new length of the array." - }, - { - "filePath": "/server/types.ts", - "syntaxKind": "MethodSignature", - "name": "concat", - "value": "{ (...items: ConcatArray[]): JSONValue[]; (...items: (JSONValue | ConcatArray)[]): JSONValue[]; }", - "description": "Combines two or more arrays.\r\nThis method returns a new array without modifying any existing arrays." - }, - { - "filePath": "/server/types.ts", - "syntaxKind": "MethodSignature", - "name": "join", - "value": "(separator?: string) => string", - "description": "Adds all the elements of an array into a string, separated by the specified separator string." - }, - { - "filePath": "/server/types.ts", - "syntaxKind": "MethodSignature", - "name": "reverse", - "value": "() => JSONValue[]", - "description": "Reverses the elements in an array in place.\r\nThis method mutates the array and returns a reference to the same array." - }, - { - "filePath": "/server/types.ts", - "syntaxKind": "MethodSignature", - "name": "shift", - "value": "() => JSONValue", - "description": "Removes the first element from an array and returns it.\r\nIf the array is empty, undefined is returned and the array is not modified." - }, - { - "filePath": "/server/types.ts", - "syntaxKind": "MethodSignature", - "name": "slice", - "value": "(start?: number, end?: number) => JSONValue[]", - "description": "Returns a copy of a section of an array.\r\nFor both start and end, a negative index can be used to indicate an offset from the end of the array.\r\nFor example, -2 refers to the second to last element of the array." - }, - { - "filePath": "/server/types.ts", - "syntaxKind": "MethodSignature", - "name": "sort", - "value": "(compareFn?: (a: JSONValue, b: JSONValue) => number) => JSONArray", - "description": "Sorts an array in place.\r\nThis method mutates the array and returns a reference to the same array." - }, - { - "filePath": "/server/types.ts", - "syntaxKind": "MethodSignature", - "name": "splice", - "value": "{ (start: number, deleteCount?: number): JSONValue[]; (start: number, deleteCount: number, ...items: JSONValue[]): JSONValue[]; }", - "description": "Removes elements from an array and, if necessary, inserts new elements in their place, returning the deleted elements." - }, - { - "filePath": "/server/types.ts", - "syntaxKind": "MethodSignature", - "name": "unshift", - "value": "(...items: JSONValue[]) => number", - "description": "Inserts new elements at the start of an array, and returns the new length of the array." - }, - { - "filePath": "/server/types.ts", - "syntaxKind": "MethodSignature", - "name": "indexOf", - "value": "(searchElement: JSONValue, fromIndex?: number) => number", - "description": "Returns the index of the first occurrence of a value in an array, or -1 if it is not present." - }, - { - "filePath": "/server/types.ts", - "syntaxKind": "MethodSignature", - "name": "lastIndexOf", - "value": "(searchElement: JSONValue, fromIndex?: number) => number", - "description": "Returns the index of the last occurrence of a specified value in an array, or -1 if it is not present." - }, - { - "filePath": "/server/types.ts", - "syntaxKind": "MethodSignature", - "name": "every", - "value": "{ (predicate: (value: JSONValue, index: number, array: JSONValue[]) => value is S, thisArg?: any): this is S[]; (predicate: (value: JSONValue, index: number, array: JSONValue[]) => unknown, thisArg?: any): boolean; }", - "description": "Determines whether all the members of an array satisfy the specified test." - }, - { - "filePath": "/server/types.ts", - "syntaxKind": "MethodSignature", - "name": "some", - "value": "(predicate: (value: JSONValue, index: number, array: JSONValue[]) => unknown, thisArg?: any) => boolean", - "description": "Determines whether the specified callback function returns true for any element of an array." - }, - { - "filePath": "/server/types.ts", - "syntaxKind": "MethodSignature", - "name": "forEach", - "value": "(callbackfn: (value: JSONValue, index: number, array: JSONValue[]) => void, thisArg?: any) => void", - "description": "Performs the specified action for each element in an array." - }, - { - "filePath": "/server/types.ts", - "syntaxKind": "MethodSignature", - "name": "map", - "value": "(callbackfn: (value: JSONValue, index: number, array: JSONValue[]) => U, thisArg?: any) => U[]", - "description": "Calls a defined callback function on each element of an array, and returns an array that contains the results." - }, - { - "filePath": "/server/types.ts", - "syntaxKind": "MethodSignature", - "name": "filter", - "value": "{ (predicate: (value: JSONValue, index: number, array: JSONValue[]) => value is S, thisArg?: any): S[]; (predicate: (value: JSONValue, index: number, array: JSONValue[]) => unknown, thisArg?: any): JSONValue[]; }", - "description": "Returns the elements of an array that meet the condition specified in a callback function." - }, - { - "filePath": "/server/types.ts", - "syntaxKind": "MethodSignature", - "name": "reduce", - "value": "{ (callbackfn: (previousValue: JSONValue, currentValue: JSONValue, currentIndex: number, array: JSONValue[]) => JSONValue): JSONValue; (callbackfn: (previousValue: JSONValue, currentValue: JSONValue, currentIndex: number, array: JSONValue[]) => JSONValue, initialValue: JSONValue): JSONValue; (callbackfn: (previousValue: U, currentValue: JSONValue, currentIndex: number, array: JSONValue[]) => U, initialValue: U): U; }", - "description": "Calls the specified callback function for all the elements in an array. The return value of the callback function is the accumulated result, and is provided as an argument in the next call to the callback function." - }, - { - "filePath": "/server/types.ts", - "syntaxKind": "MethodSignature", - "name": "reduceRight", - "value": "{ (callbackfn: (previousValue: JSONValue, currentValue: JSONValue, currentIndex: number, array: JSONValue[]) => JSONValue): JSONValue; (callbackfn: (previousValue: JSONValue, currentValue: JSONValue, currentIndex: number, array: JSONValue[]) => JSONValue, initialValue: JSONValue): JSONValue; (callbackfn: (previousValue: U, currentValue: JSONValue, currentIndex: number, array: JSONValue[]) => U, initialValue: U): U; }", - "description": "Calls the specified callback function for all the elements in an array, in descending order. The return value of the callback function is the accumulated result, and is provided as an argument in the next call to the callback function." - }, - { - "filePath": "/server/types.ts", - "syntaxKind": "MethodSignature", - "name": "find", - "value": "{ (predicate: (this: void, value: JSONValue, index: number, obj: JSONValue[]) => value is S, thisArg?: any): S; (predicate: (value: JSONValue, index: number, obj: JSONValue[]) => unknown, thisArg?: any): JSONValue; }", - "description": "Returns the value of the first element in the array where predicate is true, and undefined\r\notherwise." - }, - { - "filePath": "/server/types.ts", - "syntaxKind": "MethodSignature", - "name": "findIndex", - "value": "(predicate: (value: JSONValue, index: number, obj: JSONValue[]) => unknown, thisArg?: any) => number", - "description": "Returns the index of the first element in the array where predicate is true, and -1\r\notherwise." - }, - { - "filePath": "/server/types.ts", - "syntaxKind": "MethodSignature", - "name": "fill", - "value": "(value: JSONValue, start?: number, end?: number) => JSONArray", - "description": "Changes all array elements from `start` to `end` index to a static `value` and returns the modified array" - }, - { - "filePath": "/server/types.ts", - "syntaxKind": "MethodSignature", - "name": "copyWithin", - "value": "(target: number, start: number, end?: number) => JSONArray", - "description": "Returns the this object after copying a section of the array identified by start and end\r\nto the same array starting at position target" + "filePath": "src/server/clients/types.ts", + "syntaxKind": "PropertySignature", + "name": "apiVersion", + "value": "ApiVersion", + "description": "", + "isOptional": true }, { - "filePath": "/server/types.ts", - "syntaxKind": "MethodSignature", - "name": "entries", - "value": "() => IterableIterator<[number, JSONValue]>", - "description": "Returns an iterable of key, value pairs for every entry in the array" + "filePath": "src/server/clients/types.ts", + "syntaxKind": "PropertySignature", + "name": "headers", + "value": "{ [key: string]: any; }", + "description": "", + "isOptional": true }, { - "filePath": "/server/types.ts", - "syntaxKind": "MethodSignature", - "name": "keys", - "value": "() => IterableIterator", - "description": "Returns an iterable of keys in the array" - }, + "filePath": "src/server/clients/types.ts", + "syntaxKind": "PropertySignature", + "name": "tries", + "value": "number", + "description": "", + "isOptional": true + } + ], + "value": "export interface GraphQLQueryOptions<\n Operation extends keyof Operations,\n Operations extends AllOperations,\n> {\n variables?: ApiClientRequestOptions['variables'];\n apiVersion?: ApiVersion;\n headers?: {[key: string]: any};\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": "/server/types.ts", - "syntaxKind": "MethodSignature", - "name": "values", - "value": "() => IterableIterator", - "description": "Returns an iterable of values in the array" + "filePath": "../../node_modules/@shopify/shopify-api/lib/types.d.ts", + "name": "October22", + "value": "2022-10" }, { - "filePath": "/server/types.ts", - "syntaxKind": "MethodSignature", - "name": "includes", - "value": "(searchElement: JSONValue, fromIndex?: number) => boolean", - "description": "Determines whether an array includes a certain element, returning true or false as appropriate." + "filePath": "../../node_modules/@shopify/shopify-api/lib/types.d.ts", + "name": "January23", + "value": "2023-01" }, { - "filePath": "/server/types.ts", - "syntaxKind": "MethodSignature", - "name": "flatMap", - "value": "(callback: (this: This, value: JSONValue, index: number, array: JSONValue[]) => U | readonly U[], thisArg?: This) => U[]", - "description": "Calls a defined callback function on each element of an array. Then, flattens the result into\r\na new array.\r\nThis is identical to a map followed by flat with depth 1." + "filePath": "../../node_modules/@shopify/shopify-api/lib/types.d.ts", + "name": "April23", + "value": "2023-04" }, { - "filePath": "/server/types.ts", - "syntaxKind": "MethodSignature", - "name": "flat", - "value": "(this: A, depth?: D) => FlatArray[]", - "description": "Returns a new array with all sub-array elements concatenated into it recursively up to the\r\nspecified depth." + "filePath": "../../node_modules/@shopify/shopify-api/lib/types.d.ts", + "name": "July23", + "value": "2023-07" }, { - "filePath": "/server/types.ts", - "syntaxKind": "MethodSignature", - "name": "__@iterator@716", - "value": "() => IterableIterator", - "description": "Iterator" + "filePath": "../../node_modules/@shopify/shopify-api/lib/types.d.ts", + "name": "October23", + "value": "2023-10" }, { - "filePath": "/server/types.ts", - "syntaxKind": "MethodSignature", - "name": "__@unscopables@718", - "value": "() => { copyWithin: boolean; entries: boolean; fill: boolean; find: boolean; findIndex: boolean; keys: boolean; values: boolean; }", - "description": "Returns an object whose properties have the value 'true'\r\nwhen they will be absent when used in a 'with' statement." + "filePath": "../../node_modules/@shopify/shopify-api/lib/types.d.ts", + "name": "January24", + "value": "2024-01" }, { - "filePath": "/server/types.ts", - "syntaxKind": "MethodSignature", - "name": "at", - "value": "(index: number) => JSONValue", - "description": "Takes an integer value and returns the item at that index, allowing for positive and negative integers. Negative integers count back from the last item in the array." + "filePath": "../../node_modules/@shopify/shopify-api/lib/types.d.ts", + "name": "Unstable", + "value": "unstable" } - ], - "value": "interface JSONArray extends Array {}" + ] }, - "WebhookContextWithSession": { - "filePath": "/server/authenticate/webhooks/types.ts", - "name": "WebhookContextWithSession", + "StorefrontContext": { + "filePath": "src/server/clients/storefront/types.ts", + "name": "StorefrontContext", "description": "", "members": [ { - "filePath": "/server/authenticate/webhooks/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." - }, - { - "filePath": "/server/authenticate/webhooks/types.ts", - "syntaxKind": "PropertySignature", - "name": "admin", - "value": "WebhookAdminContext", - "description": "An admin context for the webhook.\n\nReturned only if there is a session for the shop.", - "examples": [ - { - "title": "[V3] Webhook admin context", - "description": "With the `v3_webhookAdminContext` future flag enabled, 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.webhook(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/webhooks.tsx" - } - ] - }, - { - "title": "Webhook admin context", - "description": "Use the `admin` object in the context to interact with the Admin API. This format will be removed in V3 of the package.", - "tabs": [ - { - "code": "import { json, ActionFunctionArgs } from \"@remix-run/node\";\nimport { authenticate } from \"../shopify.server\";\n\nexport async function action({ request }: ActionFunctionArgs) {\n const { admin } = await authenticate.webhook(request);\n\n const response = await admin?.graphql.query({\n data: {\n query: `#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\n const productData = response?.body.data;\n return json({ data: productData.data });\n}", - "title": "/app/routes/webhooks.tsx" - } - ] - } - ] - }, - { - "filePath": "/server/authenticate/webhooks/types.ts", + "filePath": "src/server/clients/storefront/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": "/server/authenticate/webhooks/types.ts", - "syntaxKind": "PropertySignature", - "name": "shop", - "value": "string", - "description": "The shop where the webhook was triggered.", + "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": "Webhook shop", - "description": "Get the shop that triggered a webhook.", + "title": "Querying the GraphQL API", + "description": "Use `storefront.graphql` to make query / mutation requests.", "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" + "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": "/server/authenticate/webhooks/types.ts", - "syntaxKind": "PropertySignature", - "name": "topic", - "value": "Topics", - "description": "The topic of the webhook.", - "examples": [ + } + ], + "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}" + } + } + } + ], + "jsDocTypeExamples": [ + "AppProxyContextWithSession" + ], + "related": [ + { + "name": "Admin API context", + "subtitle": "Interact with the Admin API.", + "url": "/docs/api/shopify-app-remix/apis/admin-api" + }, + { + "name": "Storefront API context", + "subtitle": "Interact with the Storefront API.", + "url": "/docs/api/shopify-app-remix/apis/storefront-api" + } + ], + "examples": { + "description": "", + "exampleGroups": [ + { + "title": "session", + "examples": [ + { + "description": "Get the session for the shop that initiated the request to the app proxy.", + "codeblock": { + "title": "Using the session object", + "tabs": [ { - "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" - } - ] + "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};", + "language": "typescript" } ] - }, - { - "filePath": "/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": "admin", + "examples": [ + { + "description": "Use the `admin` object to interact with the REST or GraphQL APIs.", + "codeblock": { + "title": "Interacting with the Admin API", + "tabs": [ { - "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" - } - ] + "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}", + "language": "typescript" } ] - }, - { - "filePath": "/server/authenticate/webhooks/types.ts", - "syntaxKind": "PropertySignature", - "name": "payload", - "value": "JSONValue", - "description": "The payload from the webhook request.", - "examples": [ + } + } + ] + }, + { + "title": "storefront", + "examples": [ + { + "description": "Use the `storefront` object to interact with the GraphQL API.", + "codeblock": { + "title": "Interacting with the Storefront API", + "tabs": [ { - "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" - } - ] + "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}", + "language": "typescript" } ] } - ], - "value": "export interface WebhookContextWithSession<\n Future extends FutureFlagOptions,\n Resources extends ShopifyRestResources,\n Topics = string | number | symbol,\n> extends Context {\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 session: Session;\n\n /**\n * An admin context for the webhook.\n *\n * Returned only if there is a session for the shop.\n *\n * @example\n * [V3] Webhook admin context.\n * With the `v3_webhookAdminContext` future flag enabled, use the `admin` object in the context to interact with the Admin API.\n * ```ts\n * // /app/routes/webhooks.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.webhook(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 * @example\n * Webhook admin context.\n * Use the `admin` object in the context to interact with the Admin API. This format will be removed in V3 of the package.\n * ```ts\n * // /app/routes/webhooks.tsx\n * import { json, ActionFunctionArgs } from \"@remix-run/node\";\n * import { authenticate } from \"../shopify.server\";\n *\n * export async function action({ request }: ActionFunctionArgs) {\n * const { admin } = await authenticate.webhook(request);\n *\n * const response = await admin?.graphql.query({\n * data: {\n * query: `#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 *\n * const productData = response?.body.data;\n * return json({ data: productData.data });\n * }\n * ```\n */\n admin: WebhookAdminContext;\n}" - }, - "WebhookAdminContext": { - "filePath": "/server/authenticate/webhooks/types.ts", - "syntaxKind": "TypeAliasDeclaration", - "name": "WebhookAdminContext", - "value": "FeatureEnabled extends true\n ? AdminApiContext\n : LegacyWebhookAdminApiContext", - "description": "" - }, - "FeatureEnabled": { - "filePath": "/server/future/flags.ts", - "syntaxKind": "TypeAliasDeclaration", - "name": "FeatureEnabled", - "value": "Future extends FutureFlags\n ? Future[Flag] extends true\n ? true\n : false\n : false", - "description": "" - }, - "FutureFlags": { - "filePath": "/server/future/flags.ts", - "name": "FutureFlags", + } + ] + }, + { + "title": "liquid", + "examples": [ + { + "description": "Use the `liquid` helper to render a `Response` with Liquid content.", + "codeblock": { + "title": "Rendering liquid content", + "tabs": [ + { + "title": "app/routes/**\\/.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}", + "language": "typescript" + } + ] + } + } + ] + } + ] + } + }, + { + "name": "Checkout", + "description": "The `authenticate.public.checkout` function ensures that checkout extension requests are coming from Shopify, and returns helpers to respond with the correct headers.", + "category": "Authenticate", + "subCategory": "Public", + "type": "object", + "isVisualComponent": false, + "definitions": [ + { + "title": "authenticate.public.checkout", + "description": "Authenticates requests coming from Shopify checkout extensions.", + "type": "AuthenticateCheckout", + "typeDefinitions": { + "AuthenticateCheckout": { + "filePath": "src/server/authenticate/public/checkout/types.ts", + "name": "AuthenticateCheckout", "description": "", - "members": [ + "params": [ { - "filePath": "/server/future/flags.ts", - "syntaxKind": "PropertySignature", - "name": "v3_webhookAdminContext", - "value": "boolean", - "description": "When enabled, returns the same `admin` context (`AdminApiContext`) from `authenticate.webhook` that is returned from `authenticate.admin`.", - "isOptional": true, - "defaultValue": "false" + "name": "request", + "description": "", + "value": "Request", + "filePath": "src/server/authenticate/public/checkout/types.ts" }, { - "filePath": "/server/future/flags.ts", - "syntaxKind": "PropertySignature", - "name": "v3_authenticatePublic", - "value": "boolean", - "description": "When enabled authenticate.public() will not work. Use authenticate.public.checkout() instead.", + "name": "options", + "description": "", + "value": "AuthenticateCheckoutOptions", "isOptional": true, - "defaultValue": "false" + "filePath": "src/server/authenticate/public/checkout/types.ts" } ], - "value": "export interface FutureFlags {\n /**\n * When enabled, returns the same `admin` context (`AdminApiContext`) from `authenticate.webhook` that is returned from `authenticate.admin`.\n *\n * @default false\n */\n v3_webhookAdminContext?: boolean;\n\n /**\n * When enabled authenticate.public() will not work. Use authenticate.public.checkout() instead.\n *\n * @default false\n */\n v3_authenticatePublic?: boolean;\n}" - }, - "AdminContext": { - "filePath": "/server/authenticate/admin/types.ts", - "syntaxKind": "TypeAliasDeclaration", - "name": "AdminContext", - "value": "Config['isEmbeddedApp'] extends false\n ? NonEmbeddedAdminContext\n : EmbeddedAdminContext", - "description": "" + "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;" }, - "NonEmbeddedAdminContext": { - "filePath": "/server/authenticate/admin/types.ts", - "name": "NonEmbeddedAdminContext", + "AuthenticateCheckoutOptions": { + "filePath": "src/server/authenticate/public/checkout/types.ts", + "name": "AuthenticateCheckoutOptions", "description": "", "members": [ { - "filePath": "/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": "/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": "/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": "/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}" }, - "AdminApiContext": { - "filePath": "/server/clients/admin/types.ts", - "name": "AdminApiContext", + "JwtPayload": { + "filePath": "../../node_modules/@shopify/shopify-api/lib/session/types.d.ts", + "name": "JwtPayload", "description": "", "members": [ { - "filePath": "/server/clients/admin/types.ts", + "filePath": "../../node_modules/@shopify/shopify-api/lib/session/types.d.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 { admin, session } = await authenticate.admin(request);\n return json(admin.rest.resources.Order.count({ session }));\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};", - "title": "/app/routes/**\\/*.ts" - }, - { - "code": "import { 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" - } - ] - }, - { - "title": "Performing a POST request to the REST API", - "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};", - "title": "/app/routes/**\\/*.ts" - }, - { - "code": "import { 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" - } - ] - } - ] + "name": "iss", + "value": "string", + "description": "" }, { - "filePath": "/server/clients/admin/types.ts", + "filePath": "../../node_modules/@shopify/shopify-api/lib/session/types.d.ts", "syntaxKind": "PropertySignature", - "name": "graphql", - "value": "GraphQLClient", - "description": "Methods for interacting with the Shopify Admin GraphQL API\n\n\n\n\n\n\n\n\n\n", - "examples": [ + "name": "dest", + "value": "string", + "description": "" + }, + { + "filePath": "../../node_modules/@shopify/shopify-api/lib/session/types.d.ts", + "syntaxKind": "PropertySignature", + "name": "aud", + "value": "string", + "description": "" + }, + { + "filePath": "../../node_modules/@shopify/shopify-api/lib/session/types.d.ts", + "syntaxKind": "PropertySignature", + "name": "sub", + "value": "string", + "description": "" + }, + { + "filePath": "../../node_modules/@shopify/shopify-api/lib/session/types.d.ts", + "syntaxKind": "PropertySignature", + "name": "exp", + "value": "number", + "description": "" + }, + { + "filePath": "../../node_modules/@shopify/shopify-api/lib/session/types.d.ts", + "syntaxKind": "PropertySignature", + "name": "nbf", + "value": "number", + "description": "" + }, + { + "filePath": "../../node_modules/@shopify/shopify-api/lib/session/types.d.ts", + "syntaxKind": "PropertySignature", + "name": "iat", + "value": "number", + "description": "" + }, + { + "filePath": "../../node_modules/@shopify/shopify-api/lib/session/types.d.ts", + "syntaxKind": "PropertySignature", + "name": "jti", + "value": "string", + "description": "" + }, + { + "filePath": "../../node_modules/@shopify/shopify-api/lib/session/types.d.ts", + "syntaxKind": "PropertySignature", + "name": "sid", + "value": "string", + "description": "" + } + ], + "value": "export interface JwtPayload {\n iss: string;\n dest: string;\n aud: string;\n sub: string;\n exp: number;\n nbf: number;\n iat: number;\n jti: string;\n sid: string;\n}" + }, + "EnsureCORSFunction": { + "filePath": "src/server/authenticate/helpers/ensure-cors-headers.ts", + "name": "EnsureCORSFunction", + "description": "", + "members": [], + "value": "export interface EnsureCORSFunction {\n (response: Response): Response;\n}" + } + } + } + ], + "jsDocTypeExamples": [ + "CheckoutContext" + ], + "related": [], + "examples": { + "description": "", + "exampleGroups": [ + { + "title": "sessionToken", + "examples": [ + { + "description": "Get store-specific data using the `sessionToken` object.", + "codeblock": { + "title": "Using the decoded session token", + "tabs": [ { - "title": "Querying the GraphQL API", - "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" - } - ] + "title": "app/routes/public/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 } = await authenticate.public.checkout(\n request\n );\n return json(await getMyAppData({shop: sessionToken.dest}));\n};", + "language": "typescript" + } + ] + } + } + ] + }, + { + "title": "cors", + "examples": [ + { + "description": "Use the `cors` helper to ensure your app can respond to checkout extension requests.", + "codeblock": { + "title": "Setting CORS headers for a public request", + "tabs": [ + { + "title": "app/routes/public/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};", + "language": "typescript" } ] } + } + ] + } + ] + } + }, + { + "name": "Webhook", + "description": "Contains functions for verifying Shopify webhooks.\n\n> Note: The format of the `admin` object returned by this function changes with the `v3_webhookAdminContext` future flag. Learn more about [gradual feature adoption](/docs/api/shopify-app-remix/guide-future-flags).", + "category": "Authenticate", + "type": "object", + "isVisualComponent": false, + "definitions": [ + { + "title": "authenticate.webhook", + "description": "Verifies requests coming from Shopify webhooks.", + "type": "AuthenticateWebhook", + "typeDefinitions": { + "AuthenticateWebhook": { + "filePath": "src/server/authenticate/webhooks/types.ts", + "name": "AuthenticateWebhook", + "description": "", + "params": [ + { + "name": "request", + "description": "", + "value": "Request", + "filePath": "src/server/authenticate/webhooks/types.ts" + } ], - "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 graphql: GraphQLClient;\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>;" }, - "RestClientWithResources": { - "filePath": "/server/clients/admin/rest.ts", + "WebhookContext": { + "filePath": "src/server/authenticate/webhooks/types.ts", "syntaxKind": "TypeAliasDeclaration", - "name": "RestClientWithResources", - "value": "RemixRestClient & {resources: Resources}", + "name": "WebhookContext", + "value": "WebhookContextWithoutSession | WebhookContextWithSession", "description": "" }, - "BillingContext": { - "filePath": "/server/authenticate/admin/billing/types.ts", - "name": "BillingContext", + "WebhookContextWithoutSession": { + "filePath": "src/server/authenticate/webhooks/types.ts", + "name": "WebhookContextWithoutSession", "description": "", "members": [ { - "filePath": "/server/authenticate/admin/billing/types.ts", + "filePath": "src/server/authenticate/webhooks/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": "" + }, + { + "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": "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": "Using 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": "Webhook API version", + "description": "Get the API version used for webhook request.", "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 { 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": "/server/authenticate/admin/billing/types.ts", + "filePath": "src/server/authenticate/webhooks/types.ts", "syntaxKind": "PropertySignature", - "name": "request", - "value": "(options: RequestBillingOptions) => Promise", - "description": "Requests payment for the plan.", + "name": "shop", + "value": "string", + "description": "The shop where the webhook was triggered.", "examples": [ { - "title": "Using a custom return URL", - "description": "Change where the merchant is returned to after approving the purchase using the `returnUrl` option.", + "title": "Webhook shop", + "description": "Get the shop that triggered a webhook.", "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" + "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": "/server/authenticate/admin/billing/types.ts", + "filePath": "src/server/authenticate/webhooks/types.ts", "syntaxKind": "PropertySignature", - "name": "cancel", - "value": "(options: CancelBillingOptions) => Promise", - "description": "Cancels an ongoing subscription, given its ID.", + "name": "topic", + "value": "Topics", + "description": "The topic of the webhook.", "examples": [ { - "title": "Cancelling a subscription", - "description": "Use the `billing.cancel` function to cancel an active subscription with the id returned from `billing.require`.", + "title": "Webhook topic", + "description": "Get the event topic for the webhook.", "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" + "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" } ] } ] - } - ], - "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 * Using 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 */\n require: (\n options: RequireBillingOptions,\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": "/server/authenticate/admin/billing/types.ts", - "name": "RequireBillingOptions", - "description": "", - "members": [ - { - "filePath": "/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": "/server/authenticate/admin/billing/types.ts", + "filePath": "src/server/authenticate/webhooks/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." + "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": "/server/authenticate/admin/billing/types.ts", + "filePath": "src/server/authenticate/webhooks/types.ts", "syntaxKind": "PropertySignature", - "name": "isTest", - "value": "boolean", - "description": "", - "isOptional": true + "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" + } + ] + } + ] } ], - "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 WebhookContextWithoutSession\n extends Context {\n session: undefined;\n admin: undefined;\n}" }, - "RequestBillingOptions": { - "filePath": "/server/authenticate/admin/billing/types.ts", - "name": "RequestBillingOptions", + "JSONValue": { + "filePath": "src/server/types.ts", + "syntaxKind": "TypeAliasDeclaration", + "name": "JSONValue", + "value": "string | number | boolean | null | JSONObject | JSONArray", + "description": "" + }, + "JSONObject": { + "filePath": "src/server/types.ts", + "name": "JSONObject", "description": "", "members": [ { - "filePath": "/server/authenticate/admin/billing/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." - }, - { - "filePath": "/server/authenticate/admin/billing/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 - }, - { - "filePath": "/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 + "filePath": "src/server/types.ts", + "name": "[x: string]", + "value": "JSONValue" } ], - "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}" + "value": "interface JSONObject {\n [x: string]: JSONValue;\n}" }, - "CancelBillingOptions": { - "filePath": "/server/authenticate/admin/billing/types.ts", - "name": "CancelBillingOptions", + "JSONArray": { + "filePath": "src/server/types.ts", + "name": "JSONArray", "description": "", "members": [ { - "filePath": "/server/authenticate/admin/billing/types.ts", + "filePath": "src/server/types.ts", "syntaxKind": "PropertySignature", - "name": "subscriptionId", - "value": "string", - "description": "The ID of the subscription to cancel." + "name": "length", + "value": "number", + "description": "Gets or sets the length of the array. This is a number one higher than the highest index in the array." }, { - "filePath": "/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/types.ts", + "syntaxKind": "MethodSignature", + "name": "toString", + "value": "() => string", + "description": "Returns a string representation of an array." }, { - "filePath": "/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}" - }, - "EmbeddedAdminContext": { - "filePath": "/server/authenticate/admin/types.ts", - "name": "EmbeddedAdminContext", - "description": "", - "members": [ + "filePath": "src/server/types.ts", + "syntaxKind": "MethodSignature", + "name": "toLocaleString", + "value": "() => string", + "description": "Returns a string representation of an array. The elements are converted to string using their toLocaleString methods." + }, { - "filePath": "/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", - "examples": [ - { - "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\";\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" - } - ] - } - ] + "filePath": "src/server/types.ts", + "syntaxKind": "MethodSignature", + "name": "pop", + "value": "() => JSONValue", + "description": "Removes the last element from an array and returns it. If the array is empty, undefined is returned and the array is not modified." }, { - "filePath": "/server/authenticate/admin/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`.", - "examples": [ - { - "title": "Redirecting to an app route", - "description": "Use the `redirect` helper to safely redirect between pages.", - "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" - } - ] - }, - { - "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 { 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", + "syntaxKind": "MethodSignature", + "name": "push", + "value": "(...items: JSONValue[]) => number", + "description": "Appends new elements to the end of an array, and returns the new length of the array." }, { - "filePath": "/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 { 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.", - "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" - } - ] - } - ] + "filePath": "src/server/types.ts", + "syntaxKind": "MethodSignature", + "name": "concat", + "value": "{ (...items: ConcatArray[]): JSONValue[]; (...items: (JSONValue | ConcatArray)[]): JSONValue[]; }", + "description": "Combines two or more arrays. This method returns a new array without modifying any existing arrays." }, { - "filePath": "/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/types.ts", + "syntaxKind": "MethodSignature", + "name": "join", + "value": "(separator?: string) => string", + "description": "Adds all the elements of an array into a string, separated by the specified separator string." }, { - "filePath": "/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/types.ts", + "syntaxKind": "MethodSignature", + "name": "reverse", + "value": "() => JSONValue[]", + "description": "Reverses the elements in an array in place. This method mutates the array and returns a reference to the same array." }, { - "filePath": "/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" - } + "filePath": "src/server/types.ts", + "syntaxKind": "MethodSignature", + "name": "shift", + "value": "() => JSONValue", + "description": "Removes the first element from an array and returns it. If the array is empty, undefined is returned and the array is not modified." + }, + { + "filePath": "src/server/types.ts", + "syntaxKind": "MethodSignature", + "name": "slice", + "value": "(start?: number, end?: number) => JSONValue[]", + "description": "Returns a copy of a section of an array. For both start and end, a negative index can be used to indicate an offset from the end of the array. For example, -2 refers to the second to last element of the array." + }, + { + "filePath": "src/server/types.ts", + "syntaxKind": "MethodSignature", + "name": "sort", + "value": "(compareFn?: (a: JSONValue, b: JSONValue) => number) => JSONArray", + "description": "Sorts an array in place. This method mutates the array and returns a reference to the same array." + }, + { + "filePath": "src/server/types.ts", + "syntaxKind": "MethodSignature", + "name": "splice", + "value": "{ (start: number, deleteCount?: number): JSONValue[]; (start: number, deleteCount: number, ...items: JSONValue[]): JSONValue[]; }", + "description": "Removes elements from an array and, if necessary, inserts new elements in their place, returning the deleted elements." + }, + { + "filePath": "src/server/types.ts", + "syntaxKind": "MethodSignature", + "name": "unshift", + "value": "(...items: JSONValue[]) => number", + "description": "Inserts new elements at the start of an array, and returns the new length of the array." + }, + { + "filePath": "src/server/types.ts", + "syntaxKind": "MethodSignature", + "name": "indexOf", + "value": "(searchElement: JSONValue, fromIndex?: number) => number", + "description": "Returns the index of the first occurrence of a value in an array, or -1 if it is not present." + }, + { + "filePath": "src/server/types.ts", + "syntaxKind": "MethodSignature", + "name": "lastIndexOf", + "value": "(searchElement: JSONValue, fromIndex?: number) => number", + "description": "Returns the index of the last occurrence of a specified value in an array, or -1 if it is not present." + }, + { + "filePath": "src/server/types.ts", + "syntaxKind": "MethodSignature", + "name": "every", + "value": "{ (predicate: (value: JSONValue, index: number, array: JSONValue[]) => value is S, thisArg?: any): this is S[]; (predicate: (value: JSONValue, index: number, array: JSONValue[]) => unknown, thisArg?: any): boolean; }", + "description": "Determines whether all the members of an array satisfy the specified test." + }, + { + "filePath": "src/server/types.ts", + "syntaxKind": "MethodSignature", + "name": "some", + "value": "(predicate: (value: JSONValue, index: number, array: JSONValue[]) => unknown, thisArg?: any) => boolean", + "description": "Determines whether the specified callback function returns true for any element of an array." + }, + { + "filePath": "src/server/types.ts", + "syntaxKind": "MethodSignature", + "name": "forEach", + "value": "(callbackfn: (value: JSONValue, index: number, array: JSONValue[]) => void, thisArg?: any) => void", + "description": "Performs the specified action for each element in an array." + }, + { + "filePath": "src/server/types.ts", + "syntaxKind": "MethodSignature", + "name": "map", + "value": "(callbackfn: (value: JSONValue, index: number, array: JSONValue[]) => U, thisArg?: any) => U[]", + "description": "Calls a defined callback function on each element of an array, and returns an array that contains the results." + }, + { + "filePath": "src/server/types.ts", + "syntaxKind": "MethodSignature", + "name": "filter", + "value": "{ (predicate: (value: JSONValue, index: number, array: JSONValue[]) => value is S, thisArg?: any): S[]; (predicate: (value: JSONValue, index: number, array: JSONValue[]) => unknown, thisArg?: any): JSONValue[]; }", + "description": "Returns the elements of an array that meet the condition specified in a callback function." + }, + { + "filePath": "src/server/types.ts", + "syntaxKind": "MethodSignature", + "name": "reduce", + "value": "{ (callbackfn: (previousValue: JSONValue, currentValue: JSONValue, currentIndex: number, array: JSONValue[]) => JSONValue): JSONValue; (callbackfn: (previousValue: JSONValue, currentValue: JSONValue, currentIndex: number, array: JSONValue[]) => JSONValue, initialValue: JSONValue): JSONValue; (callbackfn: (previousValue: U, currentValue: JSONValue, currentIndex: number, array: JSONValue[]) => U, initialValue: U): U; }", + "description": "Calls the specified callback function for all the elements in an array. The return value of the callback function is the accumulated result, and is provided as an argument in the next call to the callback function." + }, + { + "filePath": "src/server/types.ts", + "syntaxKind": "MethodSignature", + "name": "reduceRight", + "value": "{ (callbackfn: (previousValue: JSONValue, currentValue: JSONValue, currentIndex: number, array: JSONValue[]) => JSONValue): JSONValue; (callbackfn: (previousValue: JSONValue, currentValue: JSONValue, currentIndex: number, array: JSONValue[]) => JSONValue, initialValue: JSONValue): JSONValue; (callbackfn: (previousValue: U, currentValue: JSONValue, currentIndex: number, array: JSONValue[]) => U, initialValue: U): U; }", + "description": "Calls the specified callback function for all the elements in an array, in descending order. The return value of the callback function is the accumulated result, and is provided as an argument in the next call to the callback function." + }, + { + "filePath": "src/server/types.ts", + "syntaxKind": "MethodSignature", + "name": "find", + "value": "{ (predicate: (value: JSONValue, index: number, obj: JSONValue[]) => value is S, thisArg?: any): S; (predicate: (value: JSONValue, index: number, obj: JSONValue[]) => unknown, thisArg?: any): JSONValue; }", + "description": "Returns the value of the first element in the array where predicate is true, and undefined otherwise." + }, + { + "filePath": "src/server/types.ts", + "syntaxKind": "MethodSignature", + "name": "findIndex", + "value": "(predicate: (value: JSONValue, index: number, obj: JSONValue[]) => unknown, thisArg?: any) => number", + "description": "Returns the index of the first element in the array where predicate is true, and -1 otherwise." + }, + { + "filePath": "src/server/types.ts", + "syntaxKind": "MethodSignature", + "name": "fill", + "value": "(value: JSONValue, start?: number, end?: number) => JSONArray", + "description": "Changes all array elements from `start` to `end` index to a static `value` and returns the modified array" + }, + { + "filePath": "src/server/types.ts", + "syntaxKind": "MethodSignature", + "name": "copyWithin", + "value": "(target: number, start: number, end?: number) => JSONArray", + "description": "Returns the this object after copying a section of the array identified by start and end to the same array starting at position target" + }, + { + "filePath": "src/server/types.ts", + "syntaxKind": "MethodSignature", + "name": "entries", + "value": "() => IterableIterator<[number, JSONValue]>", + "description": "Returns an iterable of key, value pairs for every entry in the array" + }, + { + "filePath": "src/server/types.ts", + "syntaxKind": "MethodSignature", + "name": "keys", + "value": "() => IterableIterator", + "description": "Returns an iterable of keys in the array" + }, + { + "filePath": "src/server/types.ts", + "syntaxKind": "MethodSignature", + "name": "values", + "value": "() => IterableIterator", + "description": "Returns an iterable of values in the array" + }, + { + "filePath": "src/server/types.ts", + "syntaxKind": "MethodSignature", + "name": "includes", + "value": "(searchElement: JSONValue, fromIndex?: number) => boolean", + "description": "Determines whether an array includes a certain element, returning true or false as appropriate." + }, + { + "filePath": "src/server/types.ts", + "syntaxKind": "MethodSignature", + "name": "flatMap", + "value": "(callback: (this: This, value: JSONValue, index: number, array: JSONValue[]) => U | readonly U[], thisArg?: This) => U[]", + "description": "Calls a defined callback function on each element of an array. Then, flattens the result into a new array. This is identical to a map followed by flat with depth 1." + }, + { + "filePath": "src/server/types.ts", + "syntaxKind": "MethodSignature", + "name": "flat", + "value": "(this: A, depth?: D) => FlatArray[]", + "description": "Returns a new array with all sub-array elements concatenated into it recursively up to the specified depth." + }, + { + "filePath": "src/server/types.ts", + "syntaxKind": "MethodSignature", + "name": "__@iterator@1657", + "value": "() => IterableIterator", + "description": "Iterator" + }, + { + "filePath": "src/server/types.ts", + "syntaxKind": "PropertySignature", + "name": "__@unscopables@1659", + "value": "{ [x: number]: boolean; length?: boolean; toString?: boolean; toLocaleString?: boolean; pop?: boolean; push?: boolean; concat?: boolean; join?: boolean; reverse?: boolean; shift?: boolean; slice?: boolean; sort?: boolean; splice?: boolean; unshift?: boolean; indexOf?: boolean; lastIndexOf?: boolean; every?: boolean; some?: boolean; forEach?: boolean; map?: boolean; filter?: boolean; reduce?: boolean; reduceRight?: boolean; find?: boolean; findIndex?: boolean; fill?: boolean; copyWithin?: boolean; entries?: boolean; keys?: boolean; values?: boolean; includes?: boolean; flatMap?: boolean; flat?: boolean; [Symbol.iterator]?: boolean; readonly [Symbol.unscopables]?: boolean; at?: boolean; }", + "description": "Is an object whose properties have the value 'true' when they will be absent when used in a 'with' statement." + }, + { + "filePath": "src/server/types.ts", + "syntaxKind": "MethodSignature", + "name": "at", + "value": "(index: number) => JSONValue", + "description": "Takes an integer value and returns the item at that index, allowing for positive and negative integers. Negative integers count back from the last item in the array." + } + ], + "value": "interface JSONArray extends Array {}" + }, + "WebhookContextWithSession": { + "filePath": "src/server/authenticate/webhooks/types.ts", + "name": "WebhookContextWithSession", + "description": "", + "members": [ + { + "filePath": "src/server/authenticate/webhooks/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." + }, + { + "filePath": "src/server/authenticate/webhooks/types.ts", + "syntaxKind": "PropertySignature", + "name": "admin", + "value": "WebhookAdminContext", + "description": "An admin context for the webhook.\n\nReturned only if there is a session for the shop.", + "examples": [ + { + "title": "[V3] Webhook admin context", + "description": "With the `v3_webhookAdminContext` future flag enabled, 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.webhook(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/webhooks.tsx" + } + ] + }, + { + "title": "Webhook admin context", + "description": "Use the `admin` object in the context to interact with the Admin API. This format will be removed in V3 of the package.", + "tabs": [ + { + "code": "import { json, ActionFunctionArgs } from \"@remix-run/node\";\nimport { authenticate } from \"../shopify.server\";\n\nexport async function action({ request }: ActionFunctionArgs) {\n const { admin } = await authenticate.webhook(request);\n\n const response = await admin?.graphql.query({\n data: {\n query: `#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\n const productData = response?.body.data;\n return json({ data: productData.data });\n}", + "title": "/app/routes/webhooks.tsx" + } + ] + } + ] + }, + { + "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" + } + ] + } + ] + } + ], + "value": "export interface WebhookContextWithSession<\n Future extends FutureFlagOptions,\n Resources extends ShopifyRestResources,\n Topics = string | number | symbol,\n> extends Context {\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 session: Session;\n\n /**\n * An admin context for the webhook.\n *\n * Returned only if there is a session for the shop.\n *\n * @example\n * [V3] Webhook admin context.\n * With the `v3_webhookAdminContext` future flag enabled, use the `admin` object in the context to interact with the Admin API.\n * ```ts\n * // /app/routes/webhooks.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.webhook(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 * @example\n * Webhook admin context.\n * Use the `admin` object in the context to interact with the Admin API. This format will be removed in V3 of the package.\n * ```ts\n * // /app/routes/webhooks.tsx\n * import { json, ActionFunctionArgs } from \"@remix-run/node\";\n * import { authenticate } from \"../shopify.server\";\n *\n * export async function action({ request }: ActionFunctionArgs) {\n * const { admin } = await authenticate.webhook(request);\n *\n * const response = await admin?.graphql.query({\n * data: {\n * query: `#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 *\n * const productData = response?.body.data;\n * return json({ data: productData.data });\n * }\n * ```\n */\n admin: WebhookAdminContext;\n}" + }, + "Session": { + "filePath": "../../node_modules/@shopify/shopify-api/lib/session/session.d.ts", + "name": "Session", + "description": "Stores App information from logged in merchants so they can make authenticated requests to the Admin API.", + "members": [ + { + "filePath": "../../node_modules/@shopify/shopify-api/lib/session/session.d.ts", + "syntaxKind": "PropertyDeclaration", + "name": "id", + "value": "string", + "description": "" + }, + { + "filePath": "../../node_modules/@shopify/shopify-api/lib/session/session.d.ts", + "syntaxKind": "PropertyDeclaration", + "name": "shop", + "value": "string", + "description": "" + }, + { + "filePath": "../../node_modules/@shopify/shopify-api/lib/session/session.d.ts", + "syntaxKind": "PropertyDeclaration", + "name": "state", + "value": "string", + "description": "" + }, + { + "filePath": "../../node_modules/@shopify/shopify-api/lib/session/session.d.ts", + "syntaxKind": "PropertyDeclaration", + "name": "isOnline", + "value": "boolean", + "description": "" + }, + { + "filePath": "../../node_modules/@shopify/shopify-api/lib/session/session.d.ts", + "syntaxKind": "PropertyDeclaration", + "name": "scope", + "value": "string", + "description": "" + }, + { + "filePath": "../../node_modules/@shopify/shopify-api/lib/session/session.d.ts", + "syntaxKind": "PropertyDeclaration", + "name": "expires", + "value": "Date", + "description": "" + }, + { + "filePath": "../../node_modules/@shopify/shopify-api/lib/session/session.d.ts", + "syntaxKind": "PropertyDeclaration", + "name": "accessToken", + "value": "string", + "description": "" + }, + { + "filePath": "../../node_modules/@shopify/shopify-api/lib/session/session.d.ts", + "syntaxKind": "PropertyDeclaration", + "name": "onlineAccessInfo", + "value": "OnlineAccessInfo", + "description": "" + }, + { + "filePath": "../../node_modules/@shopify/shopify-api/lib/session/session.d.ts", + "syntaxKind": "MethodDeclaration", + "name": "isActive", + "value": "(scopes: string | string[] | AuthScopes) => boolean", + "description": "" + }, + { + "filePath": "../../node_modules/@shopify/shopify-api/lib/session/session.d.ts", + "syntaxKind": "MethodDeclaration", + "name": "isScopeChanged", + "value": "(scopes: string | string[] | AuthScopes) => boolean", + "description": "" + }, + { + "filePath": "../../node_modules/@shopify/shopify-api/lib/session/session.d.ts", + "syntaxKind": "MethodDeclaration", + "name": "isExpired", + "value": "(withinMillisecondsOfExpiry?: number) => boolean", + "description": "" + }, + { + "filePath": "../../node_modules/@shopify/shopify-api/lib/session/session.d.ts", + "syntaxKind": "MethodDeclaration", + "name": "toObject", + "value": "() => SessionParams", + "description": "" + }, + { + "filePath": "../../node_modules/@shopify/shopify-api/lib/session/session.d.ts", + "syntaxKind": "MethodDeclaration", + "name": "equals", + "value": "(other: Session) => boolean", + "description": "" + }, + { + "filePath": "../../node_modules/@shopify/shopify-api/lib/session/session.d.ts", + "syntaxKind": "MethodDeclaration", + "name": "toPropertyArray", + "value": "() => [string, string | number | boolean][]", + "description": "" + } + ], + "value": "export declare class Session {\n static fromPropertyArray(entries: [string, string | number | boolean][]): Session;\n readonly id: string;\n shop: string;\n state: string;\n isOnline: boolean;\n scope?: string;\n expires?: Date;\n accessToken?: string;\n onlineAccessInfo?: OnlineAccessInfo;\n constructor(params: SessionParams);\n isActive(scopes: AuthScopes | string | string[]): boolean;\n isScopeChanged(scopes: AuthScopes | string | string[]): boolean;\n isExpired(withinMillisecondsOfExpiry?: number): boolean;\n toObject(): SessionParams;\n equals(other: Session | undefined): boolean;\n toPropertyArray(): [string, string | number | boolean][];\n}" + }, + "OnlineAccessInfo": { + "filePath": "../../node_modules/@shopify/shopify-api/lib/auth/oauth/types.d.ts", + "name": "OnlineAccessInfo", + "description": "", + "members": [ + { + "filePath": "../../node_modules/@shopify/shopify-api/lib/auth/oauth/types.d.ts", + "syntaxKind": "PropertySignature", + "name": "expires_in", + "value": "number", + "description": "" + }, + { + "filePath": "../../node_modules/@shopify/shopify-api/lib/auth/oauth/types.d.ts", + "syntaxKind": "PropertySignature", + "name": "associated_user_scope", + "value": "string", + "description": "" + }, + { + "filePath": "../../node_modules/@shopify/shopify-api/lib/auth/oauth/types.d.ts", + "syntaxKind": "PropertySignature", + "name": "associated_user", + "value": "{ id: number; first_name: string; last_name: string; email: string; email_verified: boolean; account_owner: boolean; locale: string; collaborator: boolean; }", + "description": "" + } + ], + "value": "export interface OnlineAccessInfo {\n expires_in: number;\n associated_user_scope: string;\n associated_user: {\n id: number;\n first_name: string;\n last_name: string;\n email: string;\n email_verified: boolean;\n account_owner: boolean;\n locale: string;\n collaborator: boolean;\n };\n}" + }, + "AuthScopes": { + "filePath": "../../node_modules/@shopify/shopify-api/lib/auth/scopes/index.d.ts", + "name": "AuthScopes", + "description": "", + "members": [ + { + "filePath": "../../node_modules/@shopify/shopify-api/lib/auth/scopes/index.d.ts", + "syntaxKind": "MethodDeclaration", + "name": "has", + "value": "(scope: string | string[] | AuthScopes) => boolean", + "description": "" + }, + { + "filePath": "../../node_modules/@shopify/shopify-api/lib/auth/scopes/index.d.ts", + "syntaxKind": "MethodDeclaration", + "name": "equals", + "value": "(otherScopes: string | string[] | AuthScopes) => boolean", + "description": "" + }, + { + "filePath": "../../node_modules/@shopify/shopify-api/lib/auth/scopes/index.d.ts", + "syntaxKind": "MethodDeclaration", + "name": "toString", + "value": "() => string", + "description": "" + }, + { + "filePath": "../../node_modules/@shopify/shopify-api/lib/auth/scopes/index.d.ts", + "syntaxKind": "MethodDeclaration", + "name": "toArray", + "value": "() => string[]", + "description": "" + } + ], + "value": "declare class AuthScopes {\n static SCOPE_DELIMITER: string;\n private compressedScopes;\n private expandedScopes;\n constructor(scopes: string | string[] | AuthScopes | undefined);\n has(scope: string | string[] | AuthScopes | undefined): boolean;\n equals(otherScopes: string | string[] | AuthScopes | undefined): boolean;\n toString(): string;\n toArray(): string[];\n private getImpliedScopes;\n}" + }, + "SessionParams": { + "filePath": "../../node_modules/@shopify/shopify-api/lib/session/types.d.ts", + "name": "SessionParams", + "description": "", + "members": [ + { + "filePath": "../../node_modules/@shopify/shopify-api/lib/session/types.d.ts", + "syntaxKind": "PropertySignature", + "name": "id", + "value": "string", + "description": "" + }, + { + "filePath": "../../node_modules/@shopify/shopify-api/lib/session/types.d.ts", + "syntaxKind": "PropertySignature", + "name": "shop", + "value": "string", + "description": "" + }, + { + "filePath": "../../node_modules/@shopify/shopify-api/lib/session/types.d.ts", + "syntaxKind": "PropertySignature", + "name": "state", + "value": "string", + "description": "" + }, + { + "filePath": "../../node_modules/@shopify/shopify-api/lib/session/types.d.ts", + "syntaxKind": "PropertySignature", + "name": "isOnline", + "value": "boolean", + "description": "" + }, + { + "filePath": "../../node_modules/@shopify/shopify-api/lib/session/types.d.ts", + "syntaxKind": "PropertySignature", + "name": "scope", + "value": "string", + "description": "", + "isOptional": true + }, + { + "filePath": "../../node_modules/@shopify/shopify-api/lib/session/types.d.ts", + "syntaxKind": "PropertySignature", + "name": "expires", + "value": "Date", + "description": "", + "isOptional": true + }, + { + "filePath": "../../node_modules/@shopify/shopify-api/lib/session/types.d.ts", + "syntaxKind": "PropertySignature", + "name": "accessToken", + "value": "string", + "description": "", + "isOptional": true + }, + { + "filePath": "../../node_modules/@shopify/shopify-api/lib/session/types.d.ts", + "syntaxKind": "PropertySignature", + "name": "onlineAccessInfo", + "value": "OnlineAccessInfo", + "description": "", + "isOptional": true + } + ], + "value": "export interface SessionParams {\n readonly id: string;\n shop: string;\n state: string;\n isOnline: boolean;\n scope?: string;\n expires?: Date;\n accessToken?: string;\n onlineAccessInfo?: OnlineAccessInfo;\n}" + }, + "WebhookAdminContext": { + "filePath": "src/server/authenticate/webhooks/types.ts", + "syntaxKind": "TypeAliasDeclaration", + "name": "WebhookAdminContext", + "value": "FeatureEnabled extends true\n ? AdminApiContext\n : LegacyWebhookAdminApiContext", + "description": "" + }, + "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 { 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.", + "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" + } + ] + } + ] + }, + { + "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 { admin, session } = await authenticate.admin(request);\n return json(admin.rest.resources.Order.count({ session }));\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};", + "title": "/app/routes/**\\/*.ts" + }, + { + "code": "import { 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" + } + ] + }, + { + "title": "Performing a POST request to the REST API", + "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};", + "title": "/app/routes/**\\/*.ts" + }, + { + "code": "import { 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/clients/admin/types.ts", + "syntaxKind": "PropertySignature", + "name": "graphql", + "value": "GraphQLClient", + "description": "Methods for interacting with the Shopify Admin GraphQL API\n\n\n\n\n\n\n\n\n\n", + "examples": [ + { + "title": "Querying the GraphQL API", + "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" + } + ] + } + ] + } + ], + "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 graphql: GraphQLClient;\n}" + }, + "RestClientWithResources": { + "filePath": "src/server/clients/admin/rest.ts", + "syntaxKind": "TypeAliasDeclaration", + "name": "RestClientWithResources", + "value": "RemixRestClient & {resources: Resources}", + "description": "" + }, + "RemixRestClient": { + "filePath": "src/server/clients/admin/rest.ts", + "name": "RemixRestClient", + "description": "", + "members": [ + { + "filePath": "src/server/clients/admin/rest.ts", + "syntaxKind": "PropertyDeclaration", + "name": "session", + "value": "Session", + "description": "" + }, + { + "filePath": "src/server/clients/admin/rest.ts", + "syntaxKind": "MethodDeclaration", + "name": "get", + "value": "(params: GetRequestParams) => Promise", + "description": "Performs a GET request on the given path." + }, + { + "filePath": "src/server/clients/admin/rest.ts", + "syntaxKind": "MethodDeclaration", + "name": "post", + "value": "(params: PostRequestParams) => Promise", + "description": "Performs a POST request on the given path." + }, + { + "filePath": "src/server/clients/admin/rest.ts", + "syntaxKind": "MethodDeclaration", + "name": "put", + "value": "(params: PostRequestParams) => Promise", + "description": "Performs a PUT request on the given path." + }, + { + "filePath": "src/server/clients/admin/rest.ts", + "syntaxKind": "MethodDeclaration", + "name": "delete", + "value": "(params: GetRequestParams) => Promise", + "description": "Performs a DELETE request on the given path." + } + ], + "value": "class RemixRestClient {\n public session: Session;\n private params: AdminClientOptions['params'];\n private handleClientError: AdminClientOptions['handleClientError'];\n\n constructor({params, session, handleClientError}: AdminClientOptions) {\n this.params = params;\n this.handleClientError = handleClientError;\n this.session = session;\n }\n\n /**\n * Performs a GET request on the given path.\n */\n public async get(params: GetRequestParams) {\n return this.makeRequest({\n method: 'GET' as RequestParams['method'],\n ...params,\n });\n }\n\n /**\n * Performs a POST request on the given path.\n */\n public async post(params: PostRequestParams) {\n return this.makeRequest({\n method: 'POST' as RequestParams['method'],\n ...params,\n });\n }\n\n /**\n * Performs a PUT request on the given path.\n */\n public async put(params: PutRequestParams) {\n return this.makeRequest({\n method: 'PUT' as RequestParams['method'],\n ...params,\n });\n }\n\n /**\n * Performs a DELETE request on the given path.\n */\n public async delete(params: DeleteRequestParams) {\n return this.makeRequest({\n method: 'DELETE' as RequestParams['method'],\n ...params,\n });\n }\n\n protected async makeRequest(params: RequestParams): Promise {\n const originalClient = new this.params.api.clients.Rest({\n session: this.session,\n });\n const originalRequest = Reflect.get(originalClient, 'request');\n\n try {\n const apiResponse = await originalRequest.call(originalClient, params);\n\n // We use a separate client for REST requests and REST resources because we want to override the API library\n // client class to return a Response object instead.\n return new Response(JSON.stringify(apiResponse.body), {\n headers: apiResponse.headers,\n });\n } catch (error) {\n if (this.handleClientError) {\n throw await this.handleClientError({\n error,\n session: this.session,\n params: this.params,\n });\n } else throw new Error(error);\n }\n }\n}" + }, + "GetRequestParams": { + "filePath": "../../node_modules/@shopify/shopify-api/lib/clients/types.d.ts", + "name": "GetRequestParams", + "description": "", + "members": [ + { + "filePath": "../../node_modules/@shopify/shopify-api/lib/clients/types.d.ts", + "syntaxKind": "PropertySignature", + "name": "path", + "value": "string", + "description": "" + }, + { + "filePath": "../../node_modules/@shopify/shopify-api/lib/clients/types.d.ts", + "syntaxKind": "PropertySignature", + "name": "type", + "value": "DataType", + "description": "", + "isOptional": true + }, + { + "filePath": "../../node_modules/@shopify/shopify-api/lib/clients/types.d.ts", + "syntaxKind": "PropertySignature", + "name": "data", + "value": "string | Record", + "description": "", + "isOptional": true + }, + { + "filePath": "../../node_modules/@shopify/shopify-api/lib/clients/types.d.ts", + "syntaxKind": "PropertySignature", + "name": "query", + "value": "SearchParams", + "description": "", + "isOptional": true + }, + { + "filePath": "../../node_modules/@shopify/shopify-api/lib/clients/types.d.ts", + "syntaxKind": "PropertySignature", + "name": "extraHeaders", + "value": "HeaderParams", + "description": "", + "isOptional": true + }, + { + "filePath": "../../node_modules/@shopify/shopify-api/lib/clients/types.d.ts", + "syntaxKind": "PropertySignature", + "name": "tries", + "value": "number", + "description": "", + "isOptional": true + } + ], + "value": "export interface GetRequestParams {\n path: string;\n type?: DataType;\n data?: Record | string;\n query?: SearchParams;\n extraHeaders?: HeaderParams;\n tries?: number;\n}" + }, + "DataType": { + "filePath": "../../node_modules/@shopify/shopify-api/lib/clients/types.d.ts", + "syntaxKind": "EnumDeclaration", + "name": "DataType", + "value": "export declare enum DataType {\n JSON = \"application/json\",\n GraphQL = \"application/graphql\",\n URLEncoded = \"application/x-www-form-urlencoded\"\n}", + "members": [ + { + "filePath": "../../node_modules/@shopify/shopify-api/lib/clients/types.d.ts", + "name": "JSON", + "value": "application/json" + }, + { + "filePath": "../../node_modules/@shopify/shopify-api/lib/clients/types.d.ts", + "name": "GraphQL", + "value": "application/graphql" + }, + { + "filePath": "../../node_modules/@shopify/shopify-api/lib/clients/types.d.ts", + "name": "URLEncoded", + "value": "application/x-www-form-urlencoded" + } + ] + }, + "HeaderParams": { + "filePath": "../../node_modules/@shopify/shopify-api/lib/clients/types.d.ts", + "syntaxKind": "TypeAliasDeclaration", + "name": "HeaderParams", + "value": "Record", + "description": "", + "members": [] + }, + "PostRequestParams": { + "filePath": "../../node_modules/@shopify/shopify-api/lib/clients/types.d.ts", + "syntaxKind": "TypeAliasDeclaration", + "name": "PostRequestParams", + "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<\n ResponseWithType>>\n>;" + }, + "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": "", + "isOptional": true + }, + { + "filePath": "src/server/clients/types.ts", + "syntaxKind": "PropertySignature", + "name": "apiVersion", + "value": "ApiVersion", + "description": "", + "isOptional": true + }, + { + "filePath": "src/server/clients/types.ts", + "syntaxKind": "PropertySignature", + "name": "headers", + "value": "{ [key: string]: any; }", + "description": "", + "isOptional": true + }, + { + "filePath": "src/server/clients/types.ts", + "syntaxKind": "PropertySignature", + "name": "tries", + "value": "number", + "description": "", + "isOptional": true + } + ], + "value": "export interface GraphQLQueryOptions<\n Operation extends keyof Operations,\n Operations extends AllOperations,\n> {\n variables?: ApiClientRequestOptions['variables'];\n apiVersion?: ApiVersion;\n headers?: {[key: string]: any};\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" + } + ] + }, + "BillingContext": { + "filePath": "src/server/authenticate/admin/billing/types.ts", + "name": "BillingContext", + "description": "", + "members": [ + { + "filePath": "src/server/authenticate/admin/billing/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.", + "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": "Using 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" + } + ] + } + ] + }, + { + "filePath": "src/server/authenticate/admin/billing/types.ts", + "syntaxKind": "PropertySignature", + "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": "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 * Using 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 */\n require: (\n options: RequireBillingOptions,\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", + "name": "RequireBillingOptions", + "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", + "syntaxKind": "PropertySignature", + "name": "isTest", + "value": "boolean", + "description": "", + "isOptional": true + } + ], + "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}" + }, + "BillingCheckResponseObject": { + "filePath": "../../node_modules/@shopify/shopify-api/lib/billing/types.d.ts", + "name": "BillingCheckResponseObject", + "description": "", + "members": [ + { + "filePath": "../../node_modules/@shopify/shopify-api/lib/billing/types.d.ts", + "syntaxKind": "PropertySignature", + "name": "hasActivePayment", + "value": "boolean", + "description": "" + }, + { + "filePath": "../../node_modules/@shopify/shopify-api/lib/billing/types.d.ts", + "syntaxKind": "PropertySignature", + "name": "oneTimePurchases", + "value": "OneTimePurchase[]", + "description": "" + }, + { + "filePath": "../../node_modules/@shopify/shopify-api/lib/billing/types.d.ts", + "syntaxKind": "PropertySignature", + "name": "appSubscriptions", + "value": "AppSubscription[]", + "description": "" + } + ], + "value": "export interface BillingCheckResponseObject {\n hasActivePayment: boolean;\n oneTimePurchases: OneTimePurchase[];\n appSubscriptions: AppSubscription[];\n}" + }, + "OneTimePurchase": { + "filePath": "../../node_modules/@shopify/shopify-api/lib/billing/types.d.ts", + "name": "OneTimePurchase", + "description": "", + "members": [ + { + "filePath": "../../node_modules/@shopify/shopify-api/lib/billing/types.d.ts", + "syntaxKind": "PropertySignature", + "name": "id", + "value": "string", + "description": "" + }, + { + "filePath": "../../node_modules/@shopify/shopify-api/lib/billing/types.d.ts", + "syntaxKind": "PropertySignature", + "name": "name", + "value": "string", + "description": "" + }, + { + "filePath": "../../node_modules/@shopify/shopify-api/lib/billing/types.d.ts", + "syntaxKind": "PropertySignature", + "name": "test", + "value": "boolean", + "description": "" + }, + { + "filePath": "../../node_modules/@shopify/shopify-api/lib/billing/types.d.ts", + "syntaxKind": "PropertySignature", + "name": "status", + "value": "string", + "description": "" + } + ], + "value": "export interface OneTimePurchase {\n id: string;\n name: string;\n test: boolean;\n status: string;\n}" + }, + "AppSubscription": { + "filePath": "../../node_modules/@shopify/shopify-api/lib/billing/types.d.ts", + "name": "AppSubscription", + "description": "", + "members": [ + { + "filePath": "../../node_modules/@shopify/shopify-api/lib/billing/types.d.ts", + "syntaxKind": "PropertySignature", + "name": "id", + "value": "string", + "description": "" + }, + { + "filePath": "../../node_modules/@shopify/shopify-api/lib/billing/types.d.ts", + "syntaxKind": "PropertySignature", + "name": "name", + "value": "string", + "description": "" + }, + { + "filePath": "../../node_modules/@shopify/shopify-api/lib/billing/types.d.ts", + "syntaxKind": "PropertySignature", + "name": "test", + "value": "boolean", + "description": "" + } + ], + "value": "export interface AppSubscription {\n id: string;\n name: string;\n test: boolean;\n}" + }, + "RequestBillingOptions": { + "filePath": "src/server/authenticate/admin/billing/types.ts", + "name": "RequestBillingOptions", + "description": "", + "members": [ + { + "filePath": "src/server/authenticate/admin/billing/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." + }, + { + "filePath": "src/server/authenticate/admin/billing/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 + }, + { + "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", + "syntaxKind": "PropertySignature", + "name": "subscriptionId", + "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", + "examples": [ + { + "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\";\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" + } + ] + } + ] + }, + { + "filePath": "src/server/authenticate/admin/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`.", + "examples": [ + { + "title": "Redirecting to an app route", + "description": "Use the `redirect` helper to safely redirect between pages.", + "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" + } + ] + }, + { + "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 { 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/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 { 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.", + "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" + } + ] + } + ] + }, + { + "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 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": "" + }, + { + "filePath": "../../node_modules/@shopify/shopify-api/lib/session/types.d.ts", + "syntaxKind": "PropertySignature", + "name": "dest", + "value": "string", + "description": "" + }, + { + "filePath": "../../node_modules/@shopify/shopify-api/lib/session/types.d.ts", + "syntaxKind": "PropertySignature", + "name": "aud", + "value": "string", + "description": "" + }, + { + "filePath": "../../node_modules/@shopify/shopify-api/lib/session/types.d.ts", + "syntaxKind": "PropertySignature", + "name": "sub", + "value": "string", + "description": "" + }, + { + "filePath": "../../node_modules/@shopify/shopify-api/lib/session/types.d.ts", + "syntaxKind": "PropertySignature", + "name": "exp", + "value": "number", + "description": "" + }, + { + "filePath": "../../node_modules/@shopify/shopify-api/lib/session/types.d.ts", + "syntaxKind": "PropertySignature", + "name": "nbf", + "value": "number", + "description": "" + }, + { + "filePath": "../../node_modules/@shopify/shopify-api/lib/session/types.d.ts", + "syntaxKind": "PropertySignature", + "name": "iat", + "value": "number", + "description": "" + }, + { + "filePath": "../../node_modules/@shopify/shopify-api/lib/session/types.d.ts", + "syntaxKind": "PropertySignature", + "name": "jti", + "value": "string", + "description": "" + }, + { + "filePath": "../../node_modules/@shopify/shopify-api/lib/session/types.d.ts", + "syntaxKind": "PropertySignature", + "name": "sid", + "value": "string", + "description": "" + } + ], + "value": "export interface JwtPayload {\n iss: string;\n dest: string;\n aud: string;\n sub: string;\n exp: number;\n nbf: number;\n iat: number;\n jti: string;\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": "" + }, + "LegacyWebhookAdminApiContext": { + "filePath": "src/server/authenticate/webhooks/types.ts", + "name": "LegacyWebhookAdminApiContext", + "description": "", + "members": [ + { + "filePath": "src/server/authenticate/webhooks/types.ts", + "syntaxKind": "PropertySignature", + "name": "rest", + "value": "RestClient & Resources", + "description": "A REST client." + }, + { + "filePath": "src/server/authenticate/webhooks/types.ts", + "syntaxKind": "PropertySignature", + "name": "graphql", + "value": "InstanceType", + "description": "A GraphQL client." + } + ], + "value": "export interface LegacyWebhookAdminApiContext<\n Resources extends ShopifyRestResources,\n> {\n /** A REST client. */\n rest: InstanceType & Resources;\n /** A GraphQL client. */\n graphql: InstanceType;\n}" + }, + "RestClient": { + "filePath": "../../node_modules/@shopify/shopify-api/lib/clients/admin/rest/client.d.ts", + "name": "RestClient", + "description": "", + "members": [ + { + "filePath": "../../node_modules/@shopify/shopify-api/lib/clients/admin/rest/client.d.ts", + "syntaxKind": "PropertyDeclaration", + "name": "loggedDeprecations", + "value": "Record", + "description": "" + }, + { + "filePath": "../../node_modules/@shopify/shopify-api/lib/clients/admin/rest/client.d.ts", + "syntaxKind": "PropertyDeclaration", + "name": "client", + "value": "AdminRestApiClient", + "description": "" + }, + { + "filePath": "../../node_modules/@shopify/shopify-api/lib/clients/admin/rest/client.d.ts", + "syntaxKind": "PropertyDeclaration", + "name": "session", + "value": "Session", + "description": "" + }, + { + "filePath": "../../node_modules/@shopify/shopify-api/lib/clients/admin/rest/client.d.ts", + "syntaxKind": "PropertyDeclaration", + "name": "apiVersion", + "value": "ApiVersion", + "description": "" + }, + { + "filePath": "../../node_modules/@shopify/shopify-api/lib/clients/admin/rest/client.d.ts", + "syntaxKind": "MethodDeclaration", + "name": "get", + "value": "(params: GetRequestParams) => Promise>", + "description": "Performs a GET request on the given path." + }, + { + "filePath": "../../node_modules/@shopify/shopify-api/lib/clients/admin/rest/client.d.ts", + "syntaxKind": "MethodDeclaration", + "name": "post", + "value": "(params: PostRequestParams) => Promise>", + "description": "Performs a POST request on the given path." + }, + { + "filePath": "../../node_modules/@shopify/shopify-api/lib/clients/admin/rest/client.d.ts", + "syntaxKind": "MethodDeclaration", + "name": "put", + "value": "(params: PostRequestParams) => Promise>", + "description": "Performs a PUT request on the given path." + }, + { + "filePath": "../../node_modules/@shopify/shopify-api/lib/clients/admin/rest/client.d.ts", + "syntaxKind": "MethodDeclaration", + "name": "delete", + "value": "(params: GetRequestParams) => Promise>", + "description": "Performs a DELETE request on the given path." + } + ], + "value": "export declare class RestClient {\n static config: ConfigInterface;\n static formatPaths: boolean;\n static LINK_HEADER_REGEXP: RegExp;\n static DEFAULT_LIMIT: string;\n static RETRY_WAIT_TIME: number;\n static readonly DEPRECATION_ALERT_DELAY = 300000;\n loggedDeprecations: Record;\n readonly client: AdminRestApiClient;\n readonly session: Session;\n readonly apiVersion: ApiVersion;\n constructor({ session, apiVersion }: RestClientParams);\n /**\n * Performs a GET request on the given path.\n */\n get(params: GetRequestParams): Promise>;\n /**\n * Performs a POST request on the given path.\n */\n post(params: PostRequestParams): Promise>;\n /**\n * Performs a PUT request on the given path.\n */\n put(params: PutRequestParams): Promise>;\n /**\n * Performs a DELETE request on the given path.\n */\n delete(params: DeleteRequestParams): Promise>;\n protected request(params: RequestParams): Promise>;\n private restClass;\n private buildRequestParams;\n private logDeprecations;\n}" + }, + "RestRequestReturn": { + "filePath": "../../node_modules/@shopify/shopify-api/lib/clients/admin/types.d.ts", + "name": "RestRequestReturn", + "description": "", + "members": [ + { + "filePath": "../../node_modules/@shopify/shopify-api/lib/clients/admin/types.d.ts", + "syntaxKind": "PropertySignature", + "name": "body", + "value": "T", + "description": "" + }, + { + "filePath": "../../node_modules/@shopify/shopify-api/lib/clients/admin/types.d.ts", + "syntaxKind": "PropertySignature", + "name": "headers", + "value": "Headers", + "description": "" + }, + { + "filePath": "../../node_modules/@shopify/shopify-api/lib/clients/admin/types.d.ts", + "syntaxKind": "PropertySignature", + "name": "pageInfo", + "value": "PageInfo", + "description": "", + "isOptional": true + } + ], + "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", + "description": "", + "members": [ + { + "filePath": "../../node_modules/@shopify/shopify-api/lib/clients/admin/types.d.ts", + "syntaxKind": "PropertySignature", + "name": "limit", + "value": "string", + "description": "" + }, + { + "filePath": "../../node_modules/@shopify/shopify-api/lib/clients/admin/types.d.ts", + "syntaxKind": "PropertySignature", + "name": "fields", + "value": "string[]", + "description": "", + "isOptional": true + }, + { + "filePath": "../../node_modules/@shopify/shopify-api/lib/clients/admin/types.d.ts", + "syntaxKind": "PropertySignature", + "name": "previousPageUrl", + "value": "string", + "description": "", + "isOptional": true + }, + { + "filePath": "../../node_modules/@shopify/shopify-api/lib/clients/admin/types.d.ts", + "syntaxKind": "PropertySignature", + "name": "nextPageUrl", + "value": "string", + "description": "", + "isOptional": true + }, + { + "filePath": "../../node_modules/@shopify/shopify-api/lib/clients/admin/types.d.ts", + "syntaxKind": "PropertySignature", + "name": "prevPage", + "value": "PageInfoParams", + "description": "", + "isOptional": true + }, + { + "filePath": "../../node_modules/@shopify/shopify-api/lib/clients/admin/types.d.ts", + "syntaxKind": "PropertySignature", + "name": "nextPage", + "value": "PageInfoParams", + "description": "", + "isOptional": true + } + ], + "value": "export interface PageInfo {\n limit: string;\n fields?: string[];\n previousPageUrl?: string;\n nextPageUrl?: string;\n prevPage?: PageInfoParams;\n nextPage?: PageInfoParams;\n}" + }, + "PageInfoParams": { + "filePath": "../../node_modules/@shopify/shopify-api/lib/clients/admin/types.d.ts", + "name": "PageInfoParams", + "description": "", + "members": [ + { + "filePath": "../../node_modules/@shopify/shopify-api/lib/clients/admin/types.d.ts", + "syntaxKind": "PropertySignature", + "name": "path", + "value": "string", + "description": "" + }, + { + "filePath": "../../node_modules/@shopify/shopify-api/lib/clients/admin/types.d.ts", + "syntaxKind": "PropertySignature", + "name": "query", + "value": "SearchParams", + "description": "" + } + ], + "value": "export interface PageInfoParams {\n path: string;\n query: SearchParams;\n}" + }, + "Shopify": { + "filePath": "../../node_modules/@shopify/shopify-api/lib/index.d.ts", + "name": "Shopify", + "description": "", + "members": [ + { + "filePath": "../../node_modules/@shopify/shopify-api/lib/index.d.ts", + "syntaxKind": "PropertySignature", + "name": "config", + "value": "ConfigInterface", + "description": "" + }, + { + "filePath": "../../node_modules/@shopify/shopify-api/lib/index.d.ts", + "syntaxKind": "PropertySignature", + "name": "clients", + "value": "ShopifyClients", + "description": "" + }, + { + "filePath": "../../node_modules/@shopify/shopify-api/lib/index.d.ts", + "syntaxKind": "PropertySignature", + "name": "auth", + "value": "ShopifyAuth", + "description": "" + }, + { + "filePath": "../../node_modules/@shopify/shopify-api/lib/index.d.ts", + "syntaxKind": "PropertySignature", + "name": "session", + "value": "ShopifySession", + "description": "" + }, + { + "filePath": "../../node_modules/@shopify/shopify-api/lib/index.d.ts", + "syntaxKind": "PropertySignature", + "name": "utils", + "value": "ShopifyUtils", + "description": "" + }, + { + "filePath": "../../node_modules/@shopify/shopify-api/lib/index.d.ts", + "syntaxKind": "PropertySignature", + "name": "webhooks", + "value": "ShopifyWebhooks", + "description": "" + }, + { + "filePath": "../../node_modules/@shopify/shopify-api/lib/index.d.ts", + "syntaxKind": "PropertySignature", + "name": "billing", + "value": "ShopifyBilling", + "description": "" + }, + { + "filePath": "../../node_modules/@shopify/shopify-api/lib/index.d.ts", + "syntaxKind": "PropertySignature", + "name": "logger", + "value": "ShopifyLogger", + "description": "" + }, + { + "filePath": "../../node_modules/@shopify/shopify-api/lib/index.d.ts", + "syntaxKind": "PropertySignature", + "name": "rest", + "value": "Resources", + "description": "" + } + ], + "value": "export interface Shopify {\n config: ConfigInterface;\n clients: ShopifyClients;\n auth: ShopifyAuth;\n session: ShopifySession;\n utils: ShopifyUtils;\n webhooks: ShopifyWebhooks;\n billing: ShopifyBilling;\n logger: ShopifyLogger;\n rest: Resources;\n}" + }, + "ConfigInterface": { + "filePath": "../../node_modules/@shopify/shopify-api/lib/base-types.d.ts", + "syntaxKind": "TypeAliasDeclaration", + "name": "ConfigInterface", + "value": "Omit & {\n apiKey: string;\n hostScheme: 'http' | 'https';\n scopes: AuthScopes;\n isCustomStoreApp: boolean;\n billing?: BillingConfig;\n logger: {\n log: LogFunction;\n level: LogSeverity;\n httpRequests: boolean;\n timestamps: boolean;\n };\n future: FutureFlagOptions;\n}", + "description": "" + }, + "Key": { + "filePath": "../../node_modules/@shopify/polaris/build/ts/src/types.d.ts", + "syntaxKind": "EnumDeclaration", + "name": "Key", + "value": "export declare enum Key {\n Backspace = 8,\n Tab = 9,\n Enter = 13,\n Shift = 16,\n Ctrl = 17,\n Alt = 18,\n Pause = 19,\n CapsLock = 20,\n Escape = 27,\n Space = 32,\n PageUp = 33,\n PageDown = 34,\n End = 35,\n Home = 36,\n LeftArrow = 37,\n UpArrow = 38,\n RightArrow = 39,\n DownArrow = 40,\n Insert = 45,\n Delete = 46,\n Key0 = 48,\n Key1 = 49,\n Key2 = 50,\n Key3 = 51,\n Key4 = 52,\n Key5 = 53,\n Key6 = 54,\n Key7 = 55,\n Key8 = 56,\n Key9 = 57,\n KeyA = 65,\n KeyB = 66,\n KeyC = 67,\n KeyD = 68,\n KeyE = 69,\n KeyF = 70,\n KeyG = 71,\n KeyH = 72,\n KeyI = 73,\n KeyJ = 74,\n KeyK = 75,\n KeyL = 76,\n KeyM = 77,\n KeyN = 78,\n KeyO = 79,\n KeyP = 80,\n KeyQ = 81,\n KeyR = 82,\n KeyS = 83,\n KeyT = 84,\n KeyU = 85,\n KeyV = 86,\n KeyW = 87,\n KeyX = 88,\n KeyY = 89,\n KeyZ = 90,\n LeftMeta = 91,\n RightMeta = 92,\n Select = 93,\n Numpad0 = 96,\n Numpad1 = 97,\n Numpad2 = 98,\n Numpad3 = 99,\n Numpad4 = 100,\n Numpad5 = 101,\n Numpad6 = 102,\n Numpad7 = 103,\n Numpad8 = 104,\n Numpad9 = 105,\n Multiply = 106,\n Add = 107,\n Subtract = 109,\n Decimal = 110,\n Divide = 111,\n F1 = 112,\n F2 = 113,\n F3 = 114,\n F4 = 115,\n F5 = 116,\n F6 = 117,\n F7 = 118,\n F8 = 119,\n F9 = 120,\n F10 = 121,\n F11 = 122,\n F12 = 123,\n NumLock = 144,\n ScrollLock = 145,\n Semicolon = 186,\n Equals = 187,\n Comma = 188,\n Dash = 189,\n Period = 190,\n ForwardSlash = 191,\n GraveAccent = 192,\n OpenBracket = 219,\n BackSlash = 220,\n CloseBracket = 221,\n SingleQuote = 222\n}", + "members": [ + { + "filePath": "../../node_modules/@shopify/polaris/build/ts/src/types.d.ts", + "name": "Backspace", + "value": 8 + }, + { + "filePath": "../../node_modules/@shopify/polaris/build/ts/src/types.d.ts", + "name": "Tab", + "value": 9 + }, + { + "filePath": "../../node_modules/@shopify/polaris/build/ts/src/types.d.ts", + "name": "Enter", + "value": 13 + }, + { + "filePath": "../../node_modules/@shopify/polaris/build/ts/src/types.d.ts", + "name": "Shift", + "value": 16 + }, + { + "filePath": "../../node_modules/@shopify/polaris/build/ts/src/types.d.ts", + "name": "Ctrl", + "value": 17 + }, + { + "filePath": "../../node_modules/@shopify/polaris/build/ts/src/types.d.ts", + "name": "Alt", + "value": 18 + }, + { + "filePath": "../../node_modules/@shopify/polaris/build/ts/src/types.d.ts", + "name": "Pause", + "value": 19 + }, + { + "filePath": "../../node_modules/@shopify/polaris/build/ts/src/types.d.ts", + "name": "CapsLock", + "value": 20 + }, + { + "filePath": "../../node_modules/@shopify/polaris/build/ts/src/types.d.ts", + "name": "Escape", + "value": 27 + }, + { + "filePath": "../../node_modules/@shopify/polaris/build/ts/src/types.d.ts", + "name": "Space", + "value": 32 + }, + { + "filePath": "../../node_modules/@shopify/polaris/build/ts/src/types.d.ts", + "name": "PageUp", + "value": 33 + }, + { + "filePath": "../../node_modules/@shopify/polaris/build/ts/src/types.d.ts", + "name": "PageDown", + "value": 34 + }, + { + "filePath": "../../node_modules/@shopify/polaris/build/ts/src/types.d.ts", + "name": "End", + "value": 35 + }, + { + "filePath": "../../node_modules/@shopify/polaris/build/ts/src/types.d.ts", + "name": "Home", + "value": 36 + }, + { + "filePath": "../../node_modules/@shopify/polaris/build/ts/src/types.d.ts", + "name": "LeftArrow", + "value": 37 + }, + { + "filePath": "../../node_modules/@shopify/polaris/build/ts/src/types.d.ts", + "name": "UpArrow", + "value": 38 + }, + { + "filePath": "../../node_modules/@shopify/polaris/build/ts/src/types.d.ts", + "name": "RightArrow", + "value": 39 + }, + { + "filePath": "../../node_modules/@shopify/polaris/build/ts/src/types.d.ts", + "name": "DownArrow", + "value": 40 + }, + { + "filePath": "../../node_modules/@shopify/polaris/build/ts/src/types.d.ts", + "name": "Insert", + "value": 45 + }, + { + "filePath": "../../node_modules/@shopify/polaris/build/ts/src/types.d.ts", + "name": "Delete", + "value": 46 + }, + { + "filePath": "../../node_modules/@shopify/polaris/build/ts/src/types.d.ts", + "name": "Key0", + "value": 48 + }, + { + "filePath": "../../node_modules/@shopify/polaris/build/ts/src/types.d.ts", + "name": "Key1", + "value": 49 + }, + { + "filePath": "../../node_modules/@shopify/polaris/build/ts/src/types.d.ts", + "name": "Key2", + "value": 50 + }, + { + "filePath": "../../node_modules/@shopify/polaris/build/ts/src/types.d.ts", + "name": "Key3", + "value": 51 + }, + { + "filePath": "../../node_modules/@shopify/polaris/build/ts/src/types.d.ts", + "name": "Key4", + "value": 52 + }, + { + "filePath": "../../node_modules/@shopify/polaris/build/ts/src/types.d.ts", + "name": "Key5", + "value": 53 + }, + { + "filePath": "../../node_modules/@shopify/polaris/build/ts/src/types.d.ts", + "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": "Numpad8", + "value": 104 + }, + { + "filePath": "../../node_modules/@shopify/polaris/build/ts/src/types.d.ts", + "name": "Numpad9", + "value": 105 + }, + { + "filePath": "../../node_modules/@shopify/polaris/build/ts/src/types.d.ts", + "name": "Multiply", + "value": 106 + }, + { + "filePath": "../../node_modules/@shopify/polaris/build/ts/src/types.d.ts", + "name": "Add", + "value": 107 + }, + { + "filePath": "../../node_modules/@shopify/polaris/build/ts/src/types.d.ts", + "name": "Subtract", + "value": 109 + }, + { + "filePath": "../../node_modules/@shopify/polaris/build/ts/src/types.d.ts", + "name": "Decimal", + "value": 110 + }, + { + "filePath": "../../node_modules/@shopify/polaris/build/ts/src/types.d.ts", + "name": "Divide", + "value": 111 + }, + { + "filePath": "../../node_modules/@shopify/polaris/build/ts/src/types.d.ts", + "name": "F1", + "value": 112 + }, + { + "filePath": "../../node_modules/@shopify/polaris/build/ts/src/types.d.ts", + "name": "F2", + "value": 113 + }, + { + "filePath": "../../node_modules/@shopify/polaris/build/ts/src/types.d.ts", + "name": "F3", + "value": 114 + }, + { + "filePath": "../../node_modules/@shopify/polaris/build/ts/src/types.d.ts", + "name": "F4", + "value": 115 + }, + { + "filePath": "../../node_modules/@shopify/polaris/build/ts/src/types.d.ts", + "name": "F5", + "value": 116 + }, + { + "filePath": "../../node_modules/@shopify/polaris/build/ts/src/types.d.ts", + "name": "F6", + "value": 117 + }, + { + "filePath": "../../node_modules/@shopify/polaris/build/ts/src/types.d.ts", + "name": "F7", + "value": 118 + }, + { + "filePath": "../../node_modules/@shopify/polaris/build/ts/src/types.d.ts", + "name": "F8", + "value": 119 + }, + { + "filePath": "../../node_modules/@shopify/polaris/build/ts/src/types.d.ts", + "name": "F9", + "value": 120 + }, + { + "filePath": "../../node_modules/@shopify/polaris/build/ts/src/types.d.ts", + "name": "F10", + "value": 121 + }, + { + "filePath": "../../node_modules/@shopify/polaris/build/ts/src/types.d.ts", + "name": "F11", + "value": 122 + }, + { + "filePath": "../../node_modules/@shopify/polaris/build/ts/src/types.d.ts", + "name": "F12", + "value": 123 + }, + { + "filePath": "../../node_modules/@shopify/polaris/build/ts/src/types.d.ts", + "name": "NumLock", + "value": 144 + }, + { + "filePath": "../../node_modules/@shopify/polaris/build/ts/src/types.d.ts", + "name": "ScrollLock", + "value": 145 + }, + { + "filePath": "../../node_modules/@shopify/polaris/build/ts/src/types.d.ts", + "name": "Semicolon", + "value": 186 + }, + { + "filePath": "../../node_modules/@shopify/polaris/build/ts/src/types.d.ts", + "name": "Equals", + "value": 187 + }, + { + "filePath": "../../node_modules/@shopify/polaris/build/ts/src/types.d.ts", + "name": "Comma", + "value": 188 + }, + { + "filePath": "../../node_modules/@shopify/polaris/build/ts/src/types.d.ts", + "name": "Dash", + "value": 189 + }, + { + "filePath": "../../node_modules/@shopify/polaris/build/ts/src/types.d.ts", + "name": "Period", + "value": 190 + }, + { + "filePath": "../../node_modules/@shopify/polaris/build/ts/src/types.d.ts", + "name": "ForwardSlash", + "value": 191 + }, + { + "filePath": "../../node_modules/@shopify/polaris/build/ts/src/types.d.ts", + "name": "GraveAccent", + "value": 192 + }, + { + "filePath": "../../node_modules/@shopify/polaris/build/ts/src/types.d.ts", + "name": "OpenBracket", + "value": 219 + }, + { + "filePath": "../../node_modules/@shopify/polaris/build/ts/src/types.d.ts", + "name": "BackSlash", + "value": 220 + }, + { + "filePath": "../../node_modules/@shopify/polaris/build/ts/src/types.d.ts", + "name": "CloseBracket", + "value": 221 + }, + { + "filePath": "../../node_modules/@shopify/polaris/build/ts/src/types.d.ts", + "name": "SingleQuote", + "value": 222 + } + ] + }, + "BillingConfig": { + "filePath": "../../node_modules/@shopify/shopify-api/lib/billing/types.d.ts", + "syntaxKind": "TypeAliasDeclaration", + "name": "BillingConfig", + "value": "Record>", + "description": "", + "members": [] + }, + "LogFunction": { + "filePath": "../../node_modules/@shopify/shopify-api/lib/base-types.d.ts", + "name": "LogFunction", + "description": "", + "params": [ + { + "name": "severity", + "description": "", + "value": "LogSeverity", + "filePath": "../../node_modules/@shopify/shopify-api/lib/base-types.d.ts" + }, + { + "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/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 + } + ] + }, + "ShopifyClients": { + "filePath": "../../node_modules/@shopify/shopify-api/lib/clients/types.d.ts", + "name": "ShopifyClients", + "description": "", + "members": [ + { + "filePath": "../../node_modules/@shopify/shopify-api/lib/clients/types.d.ts", + "syntaxKind": "PropertySignature", + "name": "Rest", + "value": "typeof RestClient", + "description": "" + }, + { + "filePath": "../../node_modules/@shopify/shopify-api/lib/clients/types.d.ts", + "syntaxKind": "PropertySignature", + "name": "Graphql", + "value": "typeof GraphqlClient", + "description": "" + }, + { + "filePath": "../../node_modules/@shopify/shopify-api/lib/clients/types.d.ts", + "syntaxKind": "PropertySignature", + "name": "Storefront", + "value": "typeof StorefrontClient", + "description": "" + }, + { + "filePath": "../../node_modules/@shopify/shopify-api/lib/clients/types.d.ts", + "syntaxKind": "PropertySignature", + "name": "graphqlProxy", + "value": "GraphqlProxy", + "description": "" + } + ], + "value": "export interface ShopifyClients {\n Rest: typeof RestClient;\n Graphql: typeof GraphqlClient;\n Storefront: typeof StorefrontClient;\n graphqlProxy: GraphqlProxy;\n}" + }, + "GraphqlClient": { + "filePath": "../../node_modules/@shopify/shopify-api/lib/clients/admin/graphql/client.d.ts", + "name": "GraphqlClient", + "description": "", + "members": [ + { + "filePath": "../../node_modules/@shopify/shopify-api/lib/clients/admin/graphql/client.d.ts", + "syntaxKind": "PropertyDeclaration", + "name": "session", + "value": "Session", + "description": "" + }, + { + "filePath": "../../node_modules/@shopify/shopify-api/lib/clients/admin/graphql/client.d.ts", + "syntaxKind": "PropertyDeclaration", + "name": "client", + "value": "AdminApiClient", + "description": "" + }, + { + "filePath": "../../node_modules/@shopify/shopify-api/lib/clients/admin/graphql/client.d.ts", + "syntaxKind": "PropertyDeclaration", + "name": "apiVersion", + "value": "ApiVersion", + "description": "" + }, + { + "filePath": "../../node_modules/@shopify/shopify-api/lib/clients/admin/graphql/client.d.ts", + "syntaxKind": "MethodDeclaration", + "name": "query", + "value": "(params: GraphqlParams) => Promise>", + "description": "" + }, + { + "filePath": "../../node_modules/@shopify/shopify-api/lib/clients/admin/graphql/client.d.ts", + "syntaxKind": "MethodDeclaration", + "name": "request", + "value": "(operation: Operation, options?: GraphqlQueryOptions) => Promise : T>>", + "description": "" + } + ], + "value": "export declare class GraphqlClient {\n static config: ConfigInterface;\n readonly session: Session;\n readonly client: AdminApiClient;\n readonly apiVersion?: ApiVersion;\n constructor(params: GraphqlClientParams);\n query(params: GraphqlParams): Promise>;\n request(operation: Operation, options?: GraphqlQueryOptions): Promise : T>>;\n private graphqlClass;\n}" + }, + "GraphqlParams": { + "filePath": "../../node_modules/@shopify/shopify-api/lib/clients/types.d.ts", + "syntaxKind": "TypeAliasDeclaration", + "name": "GraphqlParams", + "value": "Omit", + "description": "", + "members": [] + }, + "RequestReturn": { + "filePath": "../../node_modules/@shopify/shopify-api/lib/clients/types.d.ts", + "name": "RequestReturn", + "description": "", + "members": [ + { + "filePath": "../../node_modules/@shopify/shopify-api/lib/clients/types.d.ts", + "syntaxKind": "PropertySignature", + "name": "body", + "value": "T", + "description": "" + }, + { + "filePath": "../../node_modules/@shopify/shopify-api/lib/clients/types.d.ts", + "syntaxKind": "PropertySignature", + "name": "headers", + "value": "Headers", + "description": "" + } + ], + "value": "export interface RequestReturn {\n body: T;\n headers: Headers;\n}" + }, + "GraphqlQueryOptions": { + "filePath": "../../node_modules/@shopify/shopify-api/lib/clients/types.d.ts", + "name": "GraphqlQueryOptions", + "description": "", + "members": [ + { + "filePath": "../../node_modules/@shopify/shopify-api/lib/clients/types.d.ts", + "syntaxKind": "PropertySignature", + "name": "variables", + "value": "ApiClientRequestOptions[\"variables\"]", + "description": "", + "isOptional": true + }, + { + "filePath": "../../node_modules/@shopify/shopify-api/lib/clients/types.d.ts", + "syntaxKind": "PropertySignature", + "name": "headers", + "value": "Record", + "description": "", + "isOptional": true + }, + { + "filePath": "../../node_modules/@shopify/shopify-api/lib/clients/types.d.ts", + "syntaxKind": "PropertySignature", + "name": "retries", + "value": "number", + "description": "", + "isOptional": true + } + ], + "value": "export interface GraphqlQueryOptions {\n variables?: ApiClientRequestOptions['variables'];\n headers?: Record;\n retries?: number;\n}" + }, + "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": "apiVersion", + "value": "ApiVersion", + "description": "" + }, + { + "filePath": "../../node_modules/@shopify/shopify-api/lib/clients/storefront/client.d.ts", + "syntaxKind": "MethodDeclaration", + "name": "query", + "value": "(params: GraphqlParams) => Promise>", + "description": "" + }, + { + "filePath": "../../node_modules/@shopify/shopify-api/lib/clients/storefront/client.d.ts", + "syntaxKind": "MethodDeclaration", + "name": "request", + "value": "(operation: Operation, options?: GraphqlQueryOptions) => Promise : T>>", + "description": "" + } + ], + "value": "export declare class StorefrontClient {\n static config: ConfigInterface;\n readonly session: Session;\n readonly client: StorefrontApiClient;\n readonly apiVersion?: ApiVersion;\n constructor(params: GraphqlClientParams);\n query(params: GraphqlParams): Promise>;\n request(operation: Operation, options?: GraphqlQueryOptions): Promise : T>>;\n private storefrontClass;\n}" + }, + "GraphqlProxy": { + "filePath": "../../node_modules/@shopify/shopify-api/lib/clients/graphql_proxy/types.d.ts", + "name": "GraphqlProxy", + "description": "", + "params": [ + { + "name": "params", + "description": "", + "value": "GraphqlProxyParams", + "filePath": "../../node_modules/@shopify/shopify-api/lib/clients/graphql_proxy/types.d.ts" + } + ], + "returns": { + "filePath": "../../node_modules/@shopify/shopify-api/lib/clients/graphql_proxy/types.d.ts", + "description": "", + "name": "Promise", + "value": "Promise" + }, + "value": "export type GraphqlProxy = (params: GraphqlProxyParams) => Promise;" + }, + "GraphqlProxyParams": { + "filePath": "../../node_modules/@shopify/shopify-api/lib/clients/graphql_proxy/types.d.ts", + "name": "GraphqlProxyParams", + "description": "", + "members": [ + { + "filePath": "../../node_modules/@shopify/shopify-api/lib/clients/graphql_proxy/types.d.ts", + "syntaxKind": "PropertySignature", + "name": "session", + "value": "Session", + "description": "" + }, + { + "filePath": "../../node_modules/@shopify/shopify-api/lib/clients/graphql_proxy/types.d.ts", + "syntaxKind": "PropertySignature", + "name": "rawBody", + "value": "string | Record", + "description": "" + } + ], + "value": "export interface GraphqlProxyParams {\n session: Session;\n rawBody: string | Record;\n}" + }, + "ShopifyAuth": { + "filePath": "../../node_modules/@shopify/shopify-api/lib/auth/index.d.ts", + "syntaxKind": "TypeAliasDeclaration", + "name": "ShopifyAuth", + "value": "{\n begin: OAuthBegin;\n callback: OAuthCallback;\n nonce: Nonce;\n safeCompare: SafeCompare;\n getEmbeddedAppUrl: GetEmbeddedAppUrl;\n buildEmbeddedAppUrl: BuildEmbeddedAppUrl;\n} & (FeatureEnabled extends true ? {\n tokenExchange: TokenExchange;\n} : Record)", + "description": "" + }, + "OAuthBegin": { + "filePath": "../../node_modules/@shopify/shopify-api/lib/auth/oauth/oauth.d.ts", + "name": "OAuthBegin", + "description": "", + "params": [ + { + "name": "beginParams", + "description": "", + "value": "BeginParams", + "filePath": "../../node_modules/@shopify/shopify-api/lib/auth/oauth/oauth.d.ts" + } + ], + "returns": { + "filePath": "../../node_modules/@shopify/shopify-api/lib/auth/oauth/oauth.d.ts", + "description": "", + "name": "Promise", + "value": "Promise" + }, + "value": "export type OAuthBegin = (beginParams: BeginParams) => Promise;" + }, + "BeginParams": { + "filePath": "../../node_modules/@shopify/shopify-api/lib/auth/oauth/types.d.ts", + "name": "BeginParams", + "description": "", + "members": [ + { + "filePath": "../../node_modules/@shopify/shopify-api/lib/auth/oauth/types.d.ts", + "syntaxKind": "PropertySignature", + "name": "shop", + "value": "string", + "description": "" + }, + { + "filePath": "../../node_modules/@shopify/shopify-api/lib/auth/oauth/types.d.ts", + "syntaxKind": "PropertySignature", + "name": "callbackPath", + "value": "string", + "description": "" + }, + { + "filePath": "../../node_modules/@shopify/shopify-api/lib/auth/oauth/types.d.ts", + "syntaxKind": "PropertySignature", + "name": "isOnline", + "value": "boolean", + "description": "" + }, + { + "filePath": "../../node_modules/@shopify/shopify-api/lib/auth/oauth/types.d.ts", + "syntaxKind": "PropertySignature", + "name": "rawRequest", + "value": "AdapterRequest", + "description": "" + }, + { + "filePath": "../../node_modules/@shopify/shopify-api/lib/auth/oauth/types.d.ts", + "syntaxKind": "PropertySignature", + "name": "rawResponse", + "value": "AdapterResponse", + "description": "", + "isOptional": true + } + ], + "value": "export interface BeginParams extends AdapterArgs {\n shop: string;\n callbackPath: string;\n isOnline: boolean;\n}" + }, + "AdapterRequest": { + "filePath": "../../node_modules/@shopify/shopify-api/runtime/http/types.d.ts", + "syntaxKind": "TypeAliasDeclaration", + "name": "AdapterRequest", + "value": "any", + "description": "" + }, + "AdapterResponse": { + "filePath": "../../node_modules/@shopify/shopify-api/runtime/http/types.d.ts", + "syntaxKind": "TypeAliasDeclaration", + "name": "AdapterResponse", + "value": "any", + "description": "" + }, + "OAuthCallback": { + "filePath": "../../node_modules/@shopify/shopify-api/lib/auth/oauth/oauth.d.ts", + "name": "OAuthCallback", + "description": "", + "params": [ + { + "name": "callbackParams", + "description": "", + "value": "CallbackParams", + "filePath": "../../node_modules/@shopify/shopify-api/lib/auth/oauth/oauth.d.ts" + } + ], + "returns": { + "filePath": "../../node_modules/@shopify/shopify-api/lib/auth/oauth/oauth.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": "export type OAuthCallback = (callbackParams: CallbackParams) => Promise>;" + }, + "CallbackParams": { + "filePath": "../../node_modules/@shopify/shopify-api/lib/auth/oauth/types.d.ts", + "name": "CallbackParams", + "description": "", + "members": [ + { + "filePath": "../../node_modules/@shopify/shopify-api/lib/auth/oauth/types.d.ts", + "syntaxKind": "PropertySignature", + "name": "rawRequest", + "value": "AdapterRequest", + "description": "" + }, + { + "filePath": "../../node_modules/@shopify/shopify-api/lib/auth/oauth/types.d.ts", + "syntaxKind": "PropertySignature", + "name": "rawResponse", + "value": "AdapterResponse", + "description": "", + "isOptional": true + } + ], + "value": "export interface CallbackParams extends AdapterArgs {\n}" + }, + "Nonce": { + "filePath": "../../node_modules/@shopify/shopify-api/lib/auth/oauth/nonce.d.ts", + "name": "Nonce", + "description": "", + "params": [], + "returns": { + "filePath": "../../node_modules/@shopify/shopify-api/lib/auth/oauth/nonce.d.ts", + "description": "", + "name": "string", + "value": "string" + }, + "value": "export type Nonce = () => string;" + }, + "SafeCompare": { + "filePath": "../../node_modules/@shopify/shopify-api/lib/auth/oauth/safe-compare.d.ts", + "name": "SafeCompare", + "description": "", + "params": [ + { + "name": "strA", + "description": "", + "value": "string | string[] | Record | number[]", + "filePath": "../../node_modules/@shopify/shopify-api/lib/auth/oauth/safe-compare.d.ts" + }, + { + "name": "strB", + "description": "", + "value": "string | string[] | Record | number[]", + "filePath": "../../node_modules/@shopify/shopify-api/lib/auth/oauth/safe-compare.d.ts" + } + ], + "returns": { + "filePath": "../../node_modules/@shopify/shopify-api/lib/auth/oauth/safe-compare.d.ts", + "description": "", + "name": "boolean", + "value": "boolean" + }, + "value": "export type SafeCompare = (strA: string | Record | string[] | number[], strB: string | Record | string[] | number[]) => boolean;" + }, + "GetEmbeddedAppUrl": { + "filePath": "../../node_modules/@shopify/shopify-api/lib/auth/get-embedded-app-url.d.ts", + "name": "GetEmbeddedAppUrl", + "description": "", + "params": [ + { + "name": "params", + "description": "", + "value": "GetEmbeddedAppUrlParams", + "filePath": "../../node_modules/@shopify/shopify-api/lib/auth/get-embedded-app-url.d.ts" + } + ], + "returns": { + "filePath": "../../node_modules/@shopify/shopify-api/lib/auth/get-embedded-app-url.d.ts", + "description": "", + "name": "Promise", + "value": "Promise" + }, + "value": "export type GetEmbeddedAppUrl = (params: GetEmbeddedAppUrlParams) => Promise;" + }, + "GetEmbeddedAppUrlParams": { + "filePath": "../../node_modules/@shopify/shopify-api/lib/auth/types.d.ts", + "name": "GetEmbeddedAppUrlParams", + "description": "", + "members": [ + { + "filePath": "../../node_modules/@shopify/shopify-api/lib/auth/types.d.ts", + "syntaxKind": "PropertySignature", + "name": "rawRequest", + "value": "AdapterRequest", + "description": "" + }, + { + "filePath": "../../node_modules/@shopify/shopify-api/lib/auth/types.d.ts", + "syntaxKind": "PropertySignature", + "name": "rawResponse", + "value": "AdapterResponse", + "description": "", + "isOptional": true + } + ], + "value": "export interface GetEmbeddedAppUrlParams extends AdapterArgs {\n}" + }, + "BuildEmbeddedAppUrl": { + "filePath": "../../node_modules/@shopify/shopify-api/lib/auth/get-embedded-app-url.d.ts", + "name": "BuildEmbeddedAppUrl", + "description": "", + "params": [ + { + "name": "host", + "description": "", + "value": "string", + "filePath": "../../node_modules/@shopify/shopify-api/lib/auth/get-embedded-app-url.d.ts" + } + ], + "returns": { + "filePath": "../../node_modules/@shopify/shopify-api/lib/auth/get-embedded-app-url.d.ts", + "description": "", + "name": "string", + "value": "string" + }, + "value": "export type BuildEmbeddedAppUrl = (host: string) => string;" + }, + "TokenExchange": { + "filePath": "../../node_modules/@shopify/shopify-api/lib/auth/oauth/token-exchange.d.ts", + "name": "TokenExchange", + "description": "", + "params": [ + { + "name": "params", + "description": "", + "value": "TokenExchangeParams", + "filePath": "../../node_modules/@shopify/shopify-api/lib/auth/oauth/token-exchange.d.ts" + } + ], + "returns": { + "filePath": "../../node_modules/@shopify/shopify-api/lib/auth/oauth/token-exchange.d.ts", + "description": "", + "name": "Promise<{\n session: Session;\n}>", + "value": "Promise<{\n session: Session;\n}>" + }, + "value": "export type TokenExchange = (params: TokenExchangeParams) => Promise<{\n session: Session;\n}>;" + }, + "TokenExchangeParams": { + "filePath": "../../node_modules/@shopify/shopify-api/lib/auth/oauth/token-exchange.d.ts", + "name": "TokenExchangeParams", + "description": "", + "members": [ + { + "filePath": "../../node_modules/@shopify/shopify-api/lib/auth/oauth/token-exchange.d.ts", + "syntaxKind": "PropertySignature", + "name": "shop", + "value": "string", + "description": "" + }, + { + "filePath": "../../node_modules/@shopify/shopify-api/lib/auth/oauth/token-exchange.d.ts", + "syntaxKind": "PropertySignature", + "name": "sessionToken", + "value": "string", + "description": "" + }, + { + "filePath": "../../node_modules/@shopify/shopify-api/lib/auth/oauth/token-exchange.d.ts", + "syntaxKind": "PropertySignature", + "name": "requestedTokenType", + "value": "RequestedTokenType", + "description": "" + } + ], + "value": "export interface TokenExchangeParams {\n shop: string;\n sessionToken: string;\n requestedTokenType: RequestedTokenType;\n}" + }, + "RequestedTokenType": { + "filePath": "../../node_modules/@shopify/shopify-api/lib/auth/oauth/token-exchange.d.ts", + "syntaxKind": "EnumDeclaration", + "name": "RequestedTokenType", + "value": "export declare enum RequestedTokenType {\n OnlineAccessToken = \"urn:shopify:params:oauth:token-type:online-access-token\",\n OfflineAccessToken = \"urn:shopify:params:oauth:token-type:offline-access-token\"\n}", + "members": [ + { + "filePath": "../../node_modules/@shopify/shopify-api/lib/auth/oauth/token-exchange.d.ts", + "name": "OnlineAccessToken", + "value": "urn:shopify:params:oauth:token-type:online-access-token" + }, + { + "filePath": "../../node_modules/@shopify/shopify-api/lib/auth/oauth/token-exchange.d.ts", + "name": "OfflineAccessToken", + "value": "urn:shopify:params:oauth:token-type:offline-access-token" + } + ] + }, + "ShopifySession": { + "filePath": "../../node_modules/@shopify/shopify-api/lib/session/index.d.ts", + "syntaxKind": "TypeAliasDeclaration", + "name": "ShopifySession", + "value": "ReturnType", + "description": "", + "members": [ + { + "filePath": "../../node_modules/@shopify/shopify-api/lib/session/index.d.ts", + "syntaxKind": "PropertySignature", + "name": "customAppSession", + "value": "(shop: string) => Session", + "description": "" + }, + { + "filePath": "../../node_modules/@shopify/shopify-api/lib/session/index.d.ts", + "syntaxKind": "PropertySignature", + "name": "getCurrentId", + "value": "({ isOnline, ...adapterArgs }: GetCurrentSessionIdParams) => Promise", + "description": "" + }, + { + "filePath": "../../node_modules/@shopify/shopify-api/lib/session/index.d.ts", + "syntaxKind": "PropertySignature", + "name": "getOfflineId", + "value": "(shop: string) => string", + "description": "" + }, + { + "filePath": "../../node_modules/@shopify/shopify-api/lib/session/index.d.ts", + "syntaxKind": "PropertySignature", + "name": "getJwtSessionId", + "value": "(shop: string, userId: string) => string", + "description": "" + }, + { + "filePath": "../../node_modules/@shopify/shopify-api/lib/session/index.d.ts", + "syntaxKind": "PropertySignature", + "name": "decodeSessionToken", + "value": "(token: string, { checkAudience }?: DecodeSessionTokenOptions) => Promise", + "description": "" + } + ] + }, + "GetCurrentSessionIdParams": { + "filePath": "../../node_modules/@shopify/shopify-api/lib/session/types.d.ts", + "name": "GetCurrentSessionIdParams", + "description": "", + "members": [ + { + "filePath": "../../node_modules/@shopify/shopify-api/lib/session/types.d.ts", + "syntaxKind": "PropertySignature", + "name": "isOnline", + "value": "boolean", + "description": "" + }, + { + "filePath": "../../node_modules/@shopify/shopify-api/lib/session/types.d.ts", + "syntaxKind": "PropertySignature", + "name": "rawRequest", + "value": "AdapterRequest", + "description": "" + }, + { + "filePath": "../../node_modules/@shopify/shopify-api/lib/session/types.d.ts", + "syntaxKind": "PropertySignature", + "name": "rawResponse", + "value": "AdapterResponse", + "description": "", + "isOptional": true + } + ], + "value": "export interface GetCurrentSessionIdParams extends AdapterArgs {\n isOnline: boolean;\n}" + }, + "DecodeSessionTokenOptions": { + "filePath": "../../node_modules/@shopify/shopify-api/lib/session/decode-session-token.d.ts", + "name": "DecodeSessionTokenOptions", + "description": "", + "members": [ + { + "filePath": "../../node_modules/@shopify/shopify-api/lib/session/decode-session-token.d.ts", + "syntaxKind": "PropertySignature", + "name": "checkAudience", + "value": "boolean", + "description": "", + "isOptional": true + } + ], + "value": "export interface DecodeSessionTokenOptions {\n checkAudience?: boolean;\n}" + }, + "ShopifyUtils": { + "filePath": "../../node_modules/@shopify/shopify-api/lib/utils/index.d.ts", + "syntaxKind": "TypeAliasDeclaration", + "name": "ShopifyUtils", + "value": "ReturnType", + "description": "", + "members": [ + { + "filePath": "../../node_modules/@shopify/shopify-api/lib/utils/index.d.ts", + "syntaxKind": "PropertySignature", + "name": "sanitizeShop", + "value": "(shop: string, throwOnInvalid?: boolean) => string", + "description": "" + }, + { + "filePath": "../../node_modules/@shopify/shopify-api/lib/utils/index.d.ts", + "syntaxKind": "PropertySignature", + "name": "sanitizeHost", + "value": "(host: string, throwOnInvalid?: boolean) => string", + "description": "" + }, + { + "filePath": "../../node_modules/@shopify/shopify-api/lib/utils/index.d.ts", + "syntaxKind": "PropertySignature", + "name": "validateHmac", + "value": "(query: AuthQuery, { signator }?: { signator: HMACSignator; }) => Promise", + "description": "" + }, + { + "filePath": "../../node_modules/@shopify/shopify-api/lib/utils/index.d.ts", + "syntaxKind": "PropertySignature", + "name": "versionCompatible", + "value": "(referenceVersion: ApiVersion, currentVersion?: ApiVersion) => boolean", + "description": "" + }, + { + "filePath": "../../node_modules/@shopify/shopify-api/lib/utils/index.d.ts", + "syntaxKind": "PropertySignature", + "name": "versionPriorTo", + "value": "(referenceVersion: ApiVersion, currentVersion?: ApiVersion) => boolean", + "description": "" + }, + { + "filePath": "../../node_modules/@shopify/shopify-api/lib/utils/index.d.ts", + "syntaxKind": "PropertySignature", + "name": "shopAdminUrlToLegacyUrl", + "value": "(shopAdminUrl: string) => string", + "description": "" + }, + { + "filePath": "../../node_modules/@shopify/shopify-api/lib/utils/index.d.ts", + "syntaxKind": "PropertySignature", + "name": "legacyUrlToShopAdminUrl", + "value": "(legacyAdminUrl: string) => string", + "description": "" + } + ] + }, + "AuthQuery": { + "filePath": "../../node_modules/@shopify/shopify-api/lib/auth/oauth/types.d.ts", + "name": "AuthQuery", + "description": "", + "members": [ + { + "filePath": "../../node_modules/@shopify/shopify-api/lib/auth/oauth/types.d.ts", + "name": "[key: string]", + "value": "string | undefined" + }, + { + "filePath": "../../node_modules/@shopify/shopify-api/lib/auth/oauth/types.d.ts", + "syntaxKind": "PropertySignature", + "name": "hmac", + "value": "string", + "description": "", + "isOptional": true + }, + { + "filePath": "../../node_modules/@shopify/shopify-api/lib/auth/oauth/types.d.ts", + "syntaxKind": "PropertySignature", + "name": "signature", + "value": "string", + "description": "", + "isOptional": true + } + ], + "value": "export interface AuthQuery {\n [key: string]: string | undefined;\n hmac?: string;\n signature?: string;\n}" + }, + "HMACSignator": { + "filePath": "../../node_modules/@shopify/shopify-api/lib/utils/hmac-validator.d.ts", + "syntaxKind": "TypeAliasDeclaration", + "name": "HMACSignator", + "value": "'admin' | 'appProxy'", + "description": "" + }, + "ShopifyWebhooks": { + "filePath": "../../node_modules/@shopify/shopify-api/lib/webhooks/index.d.ts", + "syntaxKind": "TypeAliasDeclaration", + "name": "ShopifyWebhooks", + "value": "ReturnType", + "description": "", + "members": [ + { + "filePath": "../../node_modules/@shopify/shopify-api/lib/webhooks/index.d.ts", + "syntaxKind": "PropertySignature", + "name": "addHandlers", + "value": "(handlersToAdd: AddHandlersParams) => void", + "description": "" + }, + { + "filePath": "../../node_modules/@shopify/shopify-api/lib/webhooks/index.d.ts", + "syntaxKind": "PropertySignature", + "name": "getTopicsAdded", + "value": "() => string[]", + "description": "" + }, + { + "filePath": "../../node_modules/@shopify/shopify-api/lib/webhooks/index.d.ts", + "syntaxKind": "PropertySignature", + "name": "getHandlers", + "value": "(topic: string) => WebhookHandler[]", + "description": "" + }, + { + "filePath": "../../node_modules/@shopify/shopify-api/lib/webhooks/index.d.ts", + "syntaxKind": "PropertySignature", + "name": "register", + "value": "({ session, }: RegisterParams) => Promise", + "description": "" + }, + { + "filePath": "../../node_modules/@shopify/shopify-api/lib/webhooks/index.d.ts", + "syntaxKind": "PropertySignature", + "name": "process", + "value": "({ rawBody, ...adapterArgs }: WebhookProcessParams) => Promise", + "description": "" + }, + { + "filePath": "../../node_modules/@shopify/shopify-api/lib/webhooks/index.d.ts", + "syntaxKind": "PropertySignature", + "name": "validate", + "value": "({ rawBody, ...adapterArgs }: WebhookValidateParams) => Promise", + "description": "" + } + ] + }, + "AddHandlersParams": { + "filePath": "../../node_modules/@shopify/shopify-api/lib/webhooks/types.d.ts", + "syntaxKind": "TypeAliasDeclaration", + "name": "AddHandlersParams", + "value": "Record", + "description": "", + "members": [] + }, + "WebhookHandler": { + "filePath": "../../node_modules/@shopify/shopify-api/lib/webhooks/types.d.ts", + "syntaxKind": "TypeAliasDeclaration", + "name": "WebhookHandler", + "value": "HttpWebhookHandler | HttpWebhookHandlerWithCallback | EventBridgeWebhookHandler | PubSubWebhookHandler", + "description": "" + }, + "HttpWebhookHandler": { + "filePath": "../../node_modules/@shopify/shopify-api/lib/webhooks/types.d.ts", + "name": "HttpWebhookHandler", + "description": "", + "members": [ + { + "filePath": "../../node_modules/@shopify/shopify-api/lib/webhooks/types.d.ts", + "syntaxKind": "PropertySignature", + "name": "deliveryMethod", + "value": "DeliveryMethod.Http", + "description": "" + }, + { + "filePath": "../../node_modules/@shopify/shopify-api/lib/webhooks/types.d.ts", + "syntaxKind": "PropertySignature", + "name": "callbackUrl", + "value": "string", + "description": "" + }, + { + "filePath": "../../node_modules/@shopify/shopify-api/lib/webhooks/types.d.ts", + "syntaxKind": "PropertySignature", + "name": "id", + "value": "string", + "description": "", + "isOptional": true + }, + { + "filePath": "../../node_modules/@shopify/shopify-api/lib/webhooks/types.d.ts", + "syntaxKind": "PropertySignature", + "name": "includeFields", + "value": "string[]", + "description": "", + "isOptional": true + }, + { + "filePath": "../../node_modules/@shopify/shopify-api/lib/webhooks/types.d.ts", + "syntaxKind": "PropertySignature", + "name": "metafieldNamespaces", + "value": "string[]", + "description": "", + "isOptional": true + } + ], + "value": "export interface HttpWebhookHandler extends BaseWebhookHandler {\n deliveryMethod: DeliveryMethod.Http;\n callbackUrl: string;\n}" + }, + "DeliveryMethod": { + "filePath": "../../node_modules/@shopify/shopify-api/lib/webhooks/types.d.ts", + "syntaxKind": "EnumDeclaration", + "name": "DeliveryMethod", + "value": "export declare enum DeliveryMethod {\n Http = \"http\",\n EventBridge = \"eventbridge\",\n PubSub = \"pubsub\"\n}", + "members": [ + { + "filePath": "../../node_modules/@shopify/shopify-api/lib/webhooks/types.d.ts", + "name": "Http", + "value": "http" + }, + { + "filePath": "../../node_modules/@shopify/shopify-api/lib/webhooks/types.d.ts", + "name": "EventBridge", + "value": "eventbridge" + }, + { + "filePath": "../../node_modules/@shopify/shopify-api/lib/webhooks/types.d.ts", + "name": "PubSub", + "value": "pubsub" + } + ] + }, + "HttpWebhookHandlerWithCallback": { + "filePath": "../../node_modules/@shopify/shopify-api/lib/webhooks/types.d.ts", + "name": "HttpWebhookHandlerWithCallback", + "description": "", + "members": [ + { + "filePath": "../../node_modules/@shopify/shopify-api/lib/webhooks/types.d.ts", + "syntaxKind": "PropertySignature", + "name": "callback", + "value": "WebhookHandlerFunction", + "description": "" + }, + { + "filePath": "../../node_modules/@shopify/shopify-api/lib/webhooks/types.d.ts", + "syntaxKind": "PropertySignature", + "name": "deliveryMethod", + "value": "DeliveryMethod.Http", + "description": "" + }, + { + "filePath": "../../node_modules/@shopify/shopify-api/lib/webhooks/types.d.ts", + "syntaxKind": "PropertySignature", + "name": "callbackUrl", + "value": "string", + "description": "" + }, + { + "filePath": "../../node_modules/@shopify/shopify-api/lib/webhooks/types.d.ts", + "syntaxKind": "PropertySignature", + "name": "id", + "value": "string", + "description": "", + "isOptional": true + }, + { + "filePath": "../../node_modules/@shopify/shopify-api/lib/webhooks/types.d.ts", + "syntaxKind": "PropertySignature", + "name": "includeFields", + "value": "string[]", + "description": "", + "isOptional": true + }, + { + "filePath": "../../node_modules/@shopify/shopify-api/lib/webhooks/types.d.ts", + "syntaxKind": "PropertySignature", + "name": "metafieldNamespaces", + "value": "string[]", + "description": "", + "isOptional": true + } + ], + "value": "export interface HttpWebhookHandlerWithCallback extends HttpWebhookHandler {\n callback: WebhookHandlerFunction;\n}" + }, + "WebhookHandlerFunction": { + "filePath": "../../node_modules/@shopify/shopify-api/lib/webhooks/types.d.ts", + "name": "WebhookHandlerFunction", + "description": "", + "params": [ + { + "name": "topic", + "description": "", + "value": "string", + "filePath": "../../node_modules/@shopify/shopify-api/lib/webhooks/types.d.ts" + }, + { + "name": "shop_domain", + "description": "", + "value": "string", + "filePath": "../../node_modules/@shopify/shopify-api/lib/webhooks/types.d.ts" + }, + { + "name": "body", + "description": "", + "value": "string", + "filePath": "../../node_modules/@shopify/shopify-api/lib/webhooks/types.d.ts" + }, + { + "name": "webhookId", + "description": "", + "value": "string", + "filePath": "../../node_modules/@shopify/shopify-api/lib/webhooks/types.d.ts" + }, + { + "name": "apiVersion", + "description": "", + "value": "string", + "isOptional": true, + "filePath": "../../node_modules/@shopify/shopify-api/lib/webhooks/types.d.ts" + } + ], + "returns": { + "filePath": "../../node_modules/@shopify/shopify-api/lib/webhooks/types.d.ts", + "description": "", + "name": "Promise", + "value": "Promise" + }, + "value": "export type WebhookHandlerFunction = (topic: string, shop_domain: string, body: string, webhookId: string, apiVersion?: string) => Promise;" + }, + "EventBridgeWebhookHandler": { + "filePath": "../../node_modules/@shopify/shopify-api/lib/webhooks/types.d.ts", + "name": "EventBridgeWebhookHandler", + "description": "", + "members": [ + { + "filePath": "../../node_modules/@shopify/shopify-api/lib/webhooks/types.d.ts", + "syntaxKind": "PropertySignature", + "name": "deliveryMethod", + "value": "DeliveryMethod.EventBridge", + "description": "" + }, + { + "filePath": "../../node_modules/@shopify/shopify-api/lib/webhooks/types.d.ts", + "syntaxKind": "PropertySignature", + "name": "arn", + "value": "string", + "description": "" + }, + { + "filePath": "../../node_modules/@shopify/shopify-api/lib/webhooks/types.d.ts", + "syntaxKind": "PropertySignature", + "name": "id", + "value": "string", + "description": "", + "isOptional": true + }, + { + "filePath": "../../node_modules/@shopify/shopify-api/lib/webhooks/types.d.ts", + "syntaxKind": "PropertySignature", + "name": "includeFields", + "value": "string[]", + "description": "", + "isOptional": true + }, + { + "filePath": "../../node_modules/@shopify/shopify-api/lib/webhooks/types.d.ts", + "syntaxKind": "PropertySignature", + "name": "metafieldNamespaces", + "value": "string[]", + "description": "", + "isOptional": true + } + ], + "value": "export interface EventBridgeWebhookHandler extends BaseWebhookHandler {\n deliveryMethod: DeliveryMethod.EventBridge;\n arn: string;\n}" + }, + "PubSubWebhookHandler": { + "filePath": "../../node_modules/@shopify/shopify-api/lib/webhooks/types.d.ts", + "name": "PubSubWebhookHandler", + "description": "", + "members": [ + { + "filePath": "../../node_modules/@shopify/shopify-api/lib/webhooks/types.d.ts", + "syntaxKind": "PropertySignature", + "name": "deliveryMethod", + "value": "DeliveryMethod.PubSub", + "description": "" + }, + { + "filePath": "../../node_modules/@shopify/shopify-api/lib/webhooks/types.d.ts", + "syntaxKind": "PropertySignature", + "name": "pubSubProject", + "value": "string", + "description": "" + }, + { + "filePath": "../../node_modules/@shopify/shopify-api/lib/webhooks/types.d.ts", + "syntaxKind": "PropertySignature", + "name": "pubSubTopic", + "value": "string", + "description": "" + }, + { + "filePath": "../../node_modules/@shopify/shopify-api/lib/webhooks/types.d.ts", + "syntaxKind": "PropertySignature", + "name": "id", + "value": "string", + "description": "", + "isOptional": true + }, + { + "filePath": "../../node_modules/@shopify/shopify-api/lib/webhooks/types.d.ts", + "syntaxKind": "PropertySignature", + "name": "includeFields", + "value": "string[]", + "description": "", + "isOptional": true + }, + { + "filePath": "../../node_modules/@shopify/shopify-api/lib/webhooks/types.d.ts", + "syntaxKind": "PropertySignature", + "name": "metafieldNamespaces", + "value": "string[]", + "description": "", + "isOptional": true + } + ], + "value": "export interface PubSubWebhookHandler extends BaseWebhookHandler {\n deliveryMethod: DeliveryMethod.PubSub;\n pubSubProject: string;\n pubSubTopic: string;\n}" + }, + "RegisterParams": { + "filePath": "../../node_modules/@shopify/shopify-api/lib/webhooks/types.d.ts", + "name": "RegisterParams", + "description": "", + "members": [ + { + "filePath": "../../node_modules/@shopify/shopify-api/lib/webhooks/types.d.ts", + "syntaxKind": "PropertySignature", + "name": "session", + "value": "Session", + "description": "" + } + ], + "value": "export interface RegisterParams {\n session: Session;\n}" + }, + "RegisterReturn": { + "filePath": "../../node_modules/@shopify/shopify-api/lib/webhooks/types.d.ts", + "syntaxKind": "TypeAliasDeclaration", + "name": "RegisterReturn", + "value": "Record", + "description": "", + "members": [] + }, + "Body": { + "filePath": "../../node_modules/@shopify/shopify-api/rest/types.d.ts", + "syntaxKind": "TypeAliasDeclaration", + "name": "Body", + "value": "Record", + "description": "", + "members": [] + }, + "WebhookProcessParams": { + "filePath": "../../node_modules/@shopify/shopify-api/lib/webhooks/types.d.ts", + "name": "WebhookProcessParams", + "description": "", + "members": [ + { + "filePath": "../../node_modules/@shopify/shopify-api/lib/webhooks/types.d.ts", + "syntaxKind": "PropertySignature", + "name": "rawBody", + "value": "string", + "description": "" + }, + { + "filePath": "../../node_modules/@shopify/shopify-api/lib/webhooks/types.d.ts", + "syntaxKind": "PropertySignature", + "name": "rawRequest", + "value": "AdapterRequest", + "description": "" + }, + { + "filePath": "../../node_modules/@shopify/shopify-api/lib/webhooks/types.d.ts", + "syntaxKind": "PropertySignature", + "name": "rawResponse", + "value": "AdapterResponse", + "description": "", + "isOptional": true + } + ], + "value": "export interface WebhookProcessParams extends AdapterArgs {\n rawBody: string;\n}" + }, + "WebhookValidateParams": { + "filePath": "../../node_modules/@shopify/shopify-api/lib/webhooks/types.d.ts", + "name": "WebhookValidateParams", + "description": "", + "members": [ + { + "filePath": "../../node_modules/@shopify/shopify-api/lib/webhooks/types.d.ts", + "syntaxKind": "PropertySignature", + "name": "rawBody", + "value": "string", + "description": "" + }, + { + "filePath": "../../node_modules/@shopify/shopify-api/lib/webhooks/types.d.ts", + "syntaxKind": "PropertySignature", + "name": "rawRequest", + "value": "AdapterRequest", + "description": "" + }, + { + "filePath": "../../node_modules/@shopify/shopify-api/lib/webhooks/types.d.ts", + "syntaxKind": "PropertySignature", + "name": "rawResponse", + "value": "AdapterResponse", + "description": "", + "isOptional": true + } + ], + "value": "export interface WebhookValidateParams extends WebhookProcessParams {\n}" + }, + "WebhookValidation": { + "filePath": "../../node_modules/@shopify/shopify-api/lib/webhooks/types.d.ts", + "syntaxKind": "TypeAliasDeclaration", + "name": "WebhookValidation", + "value": "WebhookValidationValid | WebhookValidationInvalid | WebhookValidationMissingHeaders", + "description": "" + }, + "WebhookValidationValid": { + "filePath": "../../node_modules/@shopify/shopify-api/lib/webhooks/types.d.ts", + "name": "WebhookValidationValid", + "description": "", + "members": [ + { + "filePath": "../../node_modules/@shopify/shopify-api/lib/webhooks/types.d.ts", + "syntaxKind": "PropertySignature", + "name": "valid", + "value": "true", + "description": "" + }, + { + "filePath": "../../node_modules/@shopify/shopify-api/lib/webhooks/types.d.ts", + "syntaxKind": "PropertySignature", + "name": "webhookId", + "value": "string", + "description": "" + }, + { + "filePath": "../../node_modules/@shopify/shopify-api/lib/webhooks/types.d.ts", + "syntaxKind": "PropertySignature", + "name": "apiVersion", + "value": "string", + "description": "" + }, + { + "filePath": "../../node_modules/@shopify/shopify-api/lib/webhooks/types.d.ts", + "syntaxKind": "PropertySignature", + "name": "domain", + "value": "string", + "description": "" + }, + { + "filePath": "../../node_modules/@shopify/shopify-api/lib/webhooks/types.d.ts", + "syntaxKind": "PropertySignature", + "name": "hmac", + "value": "string", + "description": "" + }, + { + "filePath": "../../node_modules/@shopify/shopify-api/lib/webhooks/types.d.ts", + "syntaxKind": "PropertySignature", + "name": "topic", + "value": "string", + "description": "" + } + ], + "value": "export interface WebhookValidationValid extends WebhookFields {\n valid: true;\n}" + }, + "WebhookValidationInvalid": { + "filePath": "../../node_modules/@shopify/shopify-api/lib/webhooks/types.d.ts", + "name": "WebhookValidationInvalid", + "description": "", + "members": [ + { + "filePath": "../../node_modules/@shopify/shopify-api/lib/webhooks/types.d.ts", + "syntaxKind": "PropertySignature", + "name": "valid", + "value": "false", + "description": "" + }, + { + "filePath": "../../node_modules/@shopify/shopify-api/lib/webhooks/types.d.ts", + "syntaxKind": "PropertySignature", + "name": "reason", + "value": "WebhookValidationErrorReason", + "description": "" + } + ], + "value": "export interface WebhookValidationInvalid {\n valid: false;\n reason: WebhookValidationErrorReason;\n}" + }, + "WebhookValidationErrorReason": { + "filePath": "../../node_modules/@shopify/shopify-api/lib/webhooks/types.d.ts", + "syntaxKind": "EnumDeclaration", + "name": "WebhookValidationErrorReason", + "value": "export declare enum WebhookValidationErrorReason {\n MissingHeaders = \"missing_headers\",\n MissingBody = \"missing_body\",\n InvalidHmac = \"invalid_hmac\"\n}", + "members": [ + { + "filePath": "../../node_modules/@shopify/shopify-api/lib/webhooks/types.d.ts", + "name": "MissingHeaders", + "value": "missing_headers" + }, + { + "filePath": "../../node_modules/@shopify/shopify-api/lib/webhooks/types.d.ts", + "name": "MissingBody", + "value": "missing_body" + }, + { + "filePath": "../../node_modules/@shopify/shopify-api/lib/webhooks/types.d.ts", + "name": "InvalidHmac", + "value": "invalid_hmac" + } + ] + }, + "WebhookValidationMissingHeaders": { + "filePath": "../../node_modules/@shopify/shopify-api/lib/webhooks/types.d.ts", + "name": "WebhookValidationMissingHeaders", + "description": "", + "members": [ + { + "filePath": "../../node_modules/@shopify/shopify-api/lib/webhooks/types.d.ts", + "syntaxKind": "PropertySignature", + "name": "reason", + "value": "WebhookValidationErrorReason.MissingHeaders", + "description": "" + }, + { + "filePath": "../../node_modules/@shopify/shopify-api/lib/webhooks/types.d.ts", + "syntaxKind": "PropertySignature", + "name": "missingHeaders", + "value": "string[]", + "description": "" + }, + { + "filePath": "../../node_modules/@shopify/shopify-api/lib/webhooks/types.d.ts", + "syntaxKind": "PropertySignature", + "name": "valid", + "value": "false", + "description": "" + } + ], + "value": "export interface WebhookValidationMissingHeaders extends WebhookValidationInvalid {\n reason: WebhookValidationErrorReason.MissingHeaders;\n missingHeaders: string[];\n}" + }, + "ShopifyBilling": { + "filePath": "../../node_modules/@shopify/shopify-api/lib/billing/index.d.ts", + "syntaxKind": "TypeAliasDeclaration", + "name": "ShopifyBilling", + "value": "ReturnType", + "description": "", + "members": [ + { + "filePath": "../../node_modules/@shopify/shopify-api/lib/billing/index.d.ts", + "syntaxKind": "PropertySignature", + "name": "check", + "value": "({ session, plans, isTest, returnObject, }: Params_1) => Promise>", + "description": "" + }, + { + "filePath": "../../node_modules/@shopify/shopify-api/lib/billing/index.d.ts", + "syntaxKind": "PropertySignature", + "name": "request", + "value": "({ session, plan, isTest, returnUrl: returnUrlParam, returnObject, ...overrides }: Params_2) => Promise>", + "description": "" + }, + { + "filePath": "../../node_modules/@shopify/shopify-api/lib/billing/index.d.ts", + "syntaxKind": "PropertySignature", + "name": "cancel", + "value": "(subscriptionInfo: BillingCancelParams) => Promise", + "description": "" + }, + { + "filePath": "../../node_modules/@shopify/shopify-api/lib/billing/index.d.ts", + "syntaxKind": "PropertySignature", + "name": "subscriptions", + "value": "({ session, }: BillingSubscriptionParams) => Promise", + "description": "" + } + ] + }, + "BillingCheckParams": { + "filePath": "../../node_modules/@shopify/shopify-api/lib/billing/types.d.ts", + "name": "BillingCheckParams", + "description": "", + "members": [ + { + "filePath": "../../node_modules/@shopify/shopify-api/lib/billing/types.d.ts", + "syntaxKind": "PropertySignature", + "name": "session", + "value": "Session", + "description": "" + }, + { + "filePath": "../../node_modules/@shopify/shopify-api/lib/billing/types.d.ts", + "syntaxKind": "PropertySignature", + "name": "plans", + "value": "string | string[]", + "description": "" + }, + { + "filePath": "../../node_modules/@shopify/shopify-api/lib/billing/types.d.ts", + "syntaxKind": "PropertySignature", + "name": "isTest", + "value": "boolean", + "description": "", + "isOptional": true + }, + { + "filePath": "../../node_modules/@shopify/shopify-api/lib/billing/types.d.ts", + "syntaxKind": "PropertySignature", + "name": "returnObject", + "value": "boolean", + "description": "", + "isOptional": true + } + ], + "value": "export interface BillingCheckParams {\n session: Session;\n plans: string[] | string;\n isTest?: boolean;\n returnObject?: boolean;\n}" + }, + "BillingCheckResponse": { + "filePath": "../../node_modules/@shopify/shopify-api/lib/billing/types.d.ts", + "syntaxKind": "TypeAliasDeclaration", + "name": "BillingCheckResponse", + "value": "Params['returnObject'] extends true ? BillingCheckResponseObject : boolean", + "description": "" + }, + "BillingRequestParams": { + "filePath": "../../node_modules/@shopify/shopify-api/lib/billing/types.d.ts", + "syntaxKind": "TypeAliasDeclaration", + "name": "BillingRequestParams", + "value": "{\n session: Session;\n plan: string;\n isTest?: boolean;\n returnUrl?: string;\n returnObject?: boolean;\n} & RequestConfigOverrides", + "description": "" + }, + "RequestConfigOverrides": { + "filePath": "../../node_modules/@shopify/shopify-api/lib/billing/types.d.ts", + "syntaxKind": "TypeAliasDeclaration", + "name": "RequestConfigOverrides", + "value": "Partial | Partial | Partial", + "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": "" + }, + { + "filePath": "../../node_modules/@shopify/shopify-api/lib/billing/types.d.ts", + "syntaxKind": "PropertySignature", + "name": "amount", + "value": "number", + "description": "" + }, + { + "filePath": "../../node_modules/@shopify/shopify-api/lib/billing/types.d.ts", + "syntaxKind": "PropertySignature", + "name": "currencyCode", + "value": "string", + "description": "" + } + ], + "value": "export interface BillingConfigOneTimePlan extends BillingConfigPlan {\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/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/lib/types.d.ts", + "name": "Usage", + "value": "USAGE" + } + ] + }, + "BillingConfigSubscriptionPlan": { + "filePath": "../../node_modules/@shopify/shopify-api/lib/billing/types.d.ts", + "name": "BillingConfigSubscriptionPlan", + "description": "", + "members": [ + { + "filePath": "../../node_modules/@shopify/shopify-api/lib/billing/types.d.ts", + "syntaxKind": "PropertySignature", + "name": "interval", + "value": "Exclude", + "description": "" + }, + { + "filePath": "../../node_modules/@shopify/shopify-api/lib/billing/types.d.ts", + "syntaxKind": "PropertySignature", + "name": "trialDays", + "value": "number", + "description": "", + "isOptional": true + }, + { + "filePath": "../../node_modules/@shopify/shopify-api/lib/billing/types.d.ts", + "syntaxKind": "PropertySignature", + "name": "replacementBehavior", + "value": "BillingReplacementBehavior", + "description": "", + "isOptional": true + }, + { + "filePath": "../../node_modules/@shopify/shopify-api/lib/billing/types.d.ts", + "syntaxKind": "PropertySignature", + "name": "discount", + "value": "BillingConfigSubscriptionPlanDiscount", + "description": "", + "isOptional": true + }, + { + "filePath": "../../node_modules/@shopify/shopify-api/lib/billing/types.d.ts", + "syntaxKind": "PropertySignature", + "name": "amount", + "value": "number", + "description": "" + }, + { + "filePath": "../../node_modules/@shopify/shopify-api/lib/billing/types.d.ts", + "syntaxKind": "PropertySignature", + "name": "currencyCode", + "value": "string", + "description": "" + } + ], + "value": "export interface BillingConfigSubscriptionPlan extends BillingConfigPlan {\n interval: Exclude;\n trialDays?: number;\n replacementBehavior?: BillingReplacementBehavior;\n discount?: BillingConfigSubscriptionPlanDiscount;\n}" + }, + "RecurringBillingIntervals": { + "filePath": "../../node_modules/@shopify/shopify-api/lib/types.d.ts", + "syntaxKind": "TypeAliasDeclaration", + "name": "RecurringBillingIntervals", + "value": "RecurringBillingIntervals", + "description": "" + }, + "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" + }, + { + "filePath": "../../node_modules/@shopify/shopify-api/lib/types.d.ts", + "name": "Standard", + "value": "STANDARD" + } + ] + }, + "BillingConfigSubscriptionPlanDiscount": { + "filePath": "../../node_modules/@shopify/shopify-api/lib/billing/types.d.ts", + "name": "BillingConfigSubscriptionPlanDiscount", + "description": "", + "members": [ + { + "filePath": "../../node_modules/@shopify/shopify-api/lib/billing/types.d.ts", + "syntaxKind": "PropertySignature", + "name": "durationLimitInIntervals", + "value": "number", + "description": "", + "isOptional": true + }, + { + "filePath": "../../node_modules/@shopify/shopify-api/lib/billing/types.d.ts", + "syntaxKind": "PropertySignature", + "name": "value", + "value": "BillingConfigSubscriptionPlanDiscountAmount | BillingConfigSubscriptionPlanDiscountPercentage", + "description": "" + } + ], + "value": "export interface BillingConfigSubscriptionPlanDiscount {\n durationLimitInIntervals?: number;\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/shopify-api/lib/billing/types.d.ts", + "syntaxKind": "PropertySignature", + "name": "amount", + "value": "number", + "description": "" + }, + { + "filePath": "../../node_modules/@shopify/shopify-api/lib/billing/types.d.ts", + "syntaxKind": "PropertySignature", + "name": "percentage", + "value": "never", + "description": "", + "isOptional": true + } + ], + "value": "export interface BillingConfigSubscriptionPlanDiscountAmount {\n amount: number;\n percentage?: never;\n}" + }, + "BillingConfigSubscriptionPlanDiscountPercentage": { + "filePath": "../../node_modules/@shopify/shopify-api/lib/billing/types.d.ts", + "name": "BillingConfigSubscriptionPlanDiscountPercentage", + "description": "", + "members": [ + { + "filePath": "../../node_modules/@shopify/shopify-api/lib/billing/types.d.ts", + "syntaxKind": "PropertySignature", + "name": "amount", + "value": "never", + "description": "", + "isOptional": true + }, + { + "filePath": "../../node_modules/@shopify/shopify-api/lib/billing/types.d.ts", + "syntaxKind": "PropertySignature", + "name": "percentage", + "value": "number", + "description": "" + } + ], + "value": "export interface BillingConfigSubscriptionPlanDiscountPercentage {\n amount?: never;\n percentage: number;\n}" + }, + "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": "" + }, + { + "filePath": "../../node_modules/@shopify/shopify-api/lib/billing/types.d.ts", + "syntaxKind": "PropertySignature", + "name": "usageTerms", + "value": "string", + "description": "" + }, + { + "filePath": "../../node_modules/@shopify/shopify-api/lib/billing/types.d.ts", + "syntaxKind": "PropertySignature", + "name": "trialDays", + "value": "number", + "description": "", + "isOptional": true + }, + { + "filePath": "../../node_modules/@shopify/shopify-api/lib/billing/types.d.ts", + "syntaxKind": "PropertySignature", + "name": "replacementBehavior", + "value": "BillingReplacementBehavior", + "description": "", + "isOptional": true + }, + { + "filePath": "../../node_modules/@shopify/shopify-api/lib/billing/types.d.ts", + "syntaxKind": "PropertySignature", + "name": "amount", + "value": "number", + "description": "" + }, + { + "filePath": "../../node_modules/@shopify/shopify-api/lib/billing/types.d.ts", + "syntaxKind": "PropertySignature", + "name": "currencyCode", + "value": "string", + "description": "" + } + ], + "value": "export interface BillingConfigUsagePlan extends BillingConfigPlan {\n interval: BillingInterval.Usage;\n usageTerms: string;\n trialDays?: number;\n replacementBehavior?: BillingReplacementBehavior;\n}" + }, + "BillingRequestResponse": { + "filePath": "../../node_modules/@shopify/shopify-api/lib/billing/types.d.ts", + "syntaxKind": "TypeAliasDeclaration", + "name": "BillingRequestResponse", + "value": "Params['returnObject'] extends true ? BillingRequestResponseObject : string", + "description": "" + }, + "BillingRequestResponseObject": { + "filePath": "../../node_modules/@shopify/shopify-api/lib/billing/types.d.ts", + "name": "BillingRequestResponseObject", + "description": "", + "members": [ + { + "filePath": "../../node_modules/@shopify/shopify-api/lib/billing/types.d.ts", + "syntaxKind": "PropertySignature", + "name": "confirmationUrl", + "value": "string", + "description": "" + }, + { + "filePath": "../../node_modules/@shopify/shopify-api/lib/billing/types.d.ts", + "syntaxKind": "PropertySignature", + "name": "oneTimePurchase", + "value": "OneTimePurchase", + "description": "", + "isOptional": true + }, + { + "filePath": "../../node_modules/@shopify/shopify-api/lib/billing/types.d.ts", + "syntaxKind": "PropertySignature", + "name": "appSubscription", + "value": "AppSubscription", + "description": "", + "isOptional": true + } + ], + "value": "export interface BillingRequestResponseObject {\n confirmationUrl: string;\n oneTimePurchase?: OneTimePurchase;\n appSubscription?: AppSubscription;\n}" + }, + "BillingCancelParams": { + "filePath": "../../node_modules/@shopify/shopify-api/lib/billing/types.d.ts", + "name": "BillingCancelParams", + "description": "", + "members": [ + { + "filePath": "../../node_modules/@shopify/shopify-api/lib/billing/types.d.ts", + "syntaxKind": "PropertySignature", + "name": "session", + "value": "Session", + "description": "" + }, + { + "filePath": "../../node_modules/@shopify/shopify-api/lib/billing/types.d.ts", + "syntaxKind": "PropertySignature", + "name": "subscriptionId", + "value": "string", + "description": "" + }, + { + "filePath": "../../node_modules/@shopify/shopify-api/lib/billing/types.d.ts", + "syntaxKind": "PropertySignature", + "name": "prorate", + "value": "boolean", + "description": "", + "isOptional": true + }, + { + "filePath": "../../node_modules/@shopify/shopify-api/lib/billing/types.d.ts", + "syntaxKind": "PropertySignature", + "name": "isTest", + "value": "boolean", + "description": "", + "isOptional": true + } + ], + "value": "export interface BillingCancelParams {\n session: Session;\n subscriptionId: string;\n prorate?: boolean;\n isTest?: boolean;\n}" + }, + "BillingSubscriptionParams": { + "filePath": "../../node_modules/@shopify/shopify-api/lib/billing/types.d.ts", + "name": "BillingSubscriptionParams", + "description": "", + "members": [ + { + "filePath": "../../node_modules/@shopify/shopify-api/lib/billing/types.d.ts", + "syntaxKind": "PropertySignature", + "name": "session", + "value": "Session", + "description": "" + } + ], + "value": "export interface BillingSubscriptionParams {\n session: Session;\n}" + }, + "ActiveSubscriptions": { + "filePath": "../../node_modules/@shopify/shopify-api/lib/billing/types.d.ts", + "name": "ActiveSubscriptions", + "description": "", + "members": [ + { + "filePath": "../../node_modules/@shopify/shopify-api/lib/billing/types.d.ts", + "syntaxKind": "PropertySignature", + "name": "activeSubscriptions", + "value": "AppSubscription[]", + "description": "" + } + ], + "value": "export interface ActiveSubscriptions {\n activeSubscriptions: AppSubscription[];\n}" + }, + "ShopifyLogger": { + "filePath": "../../node_modules/@shopify/shopify-api/lib/logger/index.d.ts", + "syntaxKind": "TypeAliasDeclaration", + "name": "ShopifyLogger", + "value": "ReturnType", + "description": "", + "members": [ + { + "filePath": "../../node_modules/@shopify/shopify-api/lib/logger/index.d.ts", + "syntaxKind": "PropertySignature", + "name": "log", + "value": "LoggerFunction", + "description": "" + }, + { + "filePath": "../../node_modules/@shopify/shopify-api/lib/logger/index.d.ts", + "syntaxKind": "PropertySignature", + "name": "debug", + "value": "(message: string, context?: LogContext) => Promise", + "description": "" + }, + { + "filePath": "../../node_modules/@shopify/shopify-api/lib/logger/index.d.ts", + "syntaxKind": "PropertySignature", + "name": "info", + "value": "(message: string, context?: LogContext) => Promise", + "description": "" + }, + { + "filePath": "../../node_modules/@shopify/shopify-api/lib/logger/index.d.ts", + "syntaxKind": "PropertySignature", + "name": "warning", + "value": "(message: string, context?: LogContext) => Promise", + "description": "" + }, + { + "filePath": "../../node_modules/@shopify/shopify-api/lib/logger/index.d.ts", + "syntaxKind": "PropertySignature", + "name": "error", + "value": "(message: string, context?: LogContext) => Promise", + "description": "" + }, + { + "filePath": "../../node_modules/@shopify/shopify-api/lib/logger/index.d.ts", + "syntaxKind": "PropertySignature", + "name": "deprecated", + "value": "(version: string, message: string) => void", + "description": "" + } + ] + }, + "LoggerFunction": { + "filePath": "../../node_modules/@shopify/shopify-api/lib/logger/log.d.ts", + "name": "LoggerFunction", + "description": "", + "params": [ + { + "name": "severity", + "description": "", + "value": "LogSeverity", + "filePath": "../../node_modules/@shopify/shopify-api/lib/logger/log.d.ts" + }, + { + "name": "message", + "description": "", + "value": "string", + "filePath": "../../node_modules/@shopify/shopify-api/lib/logger/log.d.ts" + }, + { + "name": "context", + "description": "", + "value": "Record", + "isOptional": true, + "filePath": "../../node_modules/@shopify/shopify-api/lib/logger/log.d.ts" + } + ], + "returns": { + "filePath": "../../node_modules/@shopify/shopify-api/lib/logger/log.d.ts", + "description": "", + "name": "void", + "value": "void" + }, + "value": "export type LoggerFunction = (severity: LogSeverity, message: string, context?: Record) => void;" + }, + "LogContext": { + "filePath": "../../node_modules/@shopify/shopify-api/lib/logger/types.d.ts", + "syntaxKind": "TypeAliasDeclaration", + "name": "LogContext", + "value": "Record", + "description": "", + "members": [] + } + } + } + ], + "jsDocTypeExamples": [ + "WebhookContextWithSession" + ], + "related": [ + { + "name": "Admin API context", + "subtitle": "Interact with the Admin API.", + "url": "/docs/api/shopify-app-remix/apis/admin-api" + } + ], + "examples": { + "description": "", + "exampleGroups": [ + { + "title": "admin", + "examples": [ + { + "description": "With the `v3_webhookAdminContext` future flag enabled, use the `admin` object in the context to interact with the Admin API.", + "codeblock": { + "title": "[V3] Webhook admin context", + "tabs": [ + { + "title": "/app/routes/webhooks.tsx", + "code": "import { ActionFunctionArgs } from \"@remix-run/node\";\nimport { authenticate } from \"../shopify.server\";\n\nexport async function action({ request }: ActionFunctionArgs) {\n const { admin } = await authenticate.webhook(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}", + "language": "typescript" + } + ] + } + }, + { + "description": "Use the `admin` object in the context to interact with the Admin API. This format will be removed in V3 of the package.", + "codeblock": { + "title": "Webhook admin context", + "tabs": [ + { + "title": "/app/routes/webhooks.tsx", + "code": "import { json, ActionFunctionArgs } from \"@remix-run/node\";\nimport { authenticate } from \"../shopify.server\";\n\nexport async function action({ request }: ActionFunctionArgs) {\n const { admin } = await authenticate.webhook(request);\n\n const response = await admin?.graphql.query<any>({\n data: {\n query: `#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\n const productData = response?.body.data;\n return json({ data: productData.data });\n}", + "language": "typescript" + } + ] + } + } + ] + }, + { + "title": "apiVersion", + "examples": [ + { + "description": "Get the API version used for webhook request.", + "codeblock": { + "title": "Webhook API version", + "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};", + "language": "typescript" + } + ] + } + } + ] + }, + { + "title": "shop", + "examples": [ + { + "description": "Get the shop that triggered a webhook.", + "codeblock": { + "title": "Webhook shop", + "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};", + "language": "typescript" + } + ] + } + } + ] + }, + { + "title": "topic", + "examples": [ + { + "description": "Get the event topic for the webhook.", + "codeblock": { + "title": "Webhook topic", + "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};", + "language": "typescript" + } + ] + } + } + ] + }, + { + "title": "webhookId", + "examples": [ + { + "description": "Get the webhook ID.", + "codeblock": { + "title": "Webhook ID", + "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};", + "language": "typescript" + } + ] + } + } + ] + }, + { + "title": "payload", + "examples": [ + { + "description": "Get the request's POST payload.", + "codeblock": { + "title": "Webhook payload", + "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};", + "language": "typescript" + } + ] + } + } + ] + } + ] + } + }, + { + "name": "Admin API", + "description": "Contains objects used to interact with the Admin API.\n\nThis object is returned as part of different contexts, such as [`admin`](/docs/api/shopify-app-remix/authenticate/admin), [`unauthenticated.admin`](/docs/api/shopify-app-remix/unauthenticated/unauthenticated-admin), and [`webhook`](/docs/api/shopify-app-remix/authenticate/webhook).", + "category": "APIs", + "type": "object", + "isVisualComponent": false, + "definitions": [ + { + "title": "admin", + "description": "Provides utilities that apps can use to make requests to the Admin API.", + "type": "AdminApiContext", + "typeDefinitions": { + "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 { admin, session } = await authenticate.admin(request);\n return json(admin.rest.resources.Order.count({ session }));\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};", + "title": "/app/routes/**\\/*.ts" + }, + { + "code": "import { 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" + } + ] + }, + { + "title": "Performing a POST request to the REST API", + "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};", + "title": "/app/routes/**\\/*.ts" + }, + { + "code": "import { 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/clients/admin/types.ts", + "syntaxKind": "PropertySignature", + "name": "graphql", + "value": "GraphQLClient", + "description": "Methods for interacting with the Shopify Admin GraphQL API\n\n\n\n\n\n\n\n\n\n", + "examples": [ + { + "title": "Querying the GraphQL API", + "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" + } + ] + } + ] + } + ], + "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 graphql: GraphQLClient;\n}" + }, + "RestClientWithResources": { + "filePath": "src/server/clients/admin/rest.ts", + "syntaxKind": "TypeAliasDeclaration", + "name": "RestClientWithResources", + "value": "RemixRestClient & {resources: Resources}", + "description": "" + }, + "RemixRestClient": { + "filePath": "src/server/clients/admin/rest.ts", + "name": "RemixRestClient", + "description": "", + "members": [ + { + "filePath": "src/server/clients/admin/rest.ts", + "syntaxKind": "PropertyDeclaration", + "name": "session", + "value": "Session", + "description": "" + }, + { + "filePath": "src/server/clients/admin/rest.ts", + "syntaxKind": "MethodDeclaration", + "name": "get", + "value": "(params: GetRequestParams) => Promise", + "description": "Performs a GET request on the given path." + }, + { + "filePath": "src/server/clients/admin/rest.ts", + "syntaxKind": "MethodDeclaration", + "name": "post", + "value": "(params: PostRequestParams) => Promise", + "description": "Performs a POST request on the given path." + }, + { + "filePath": "src/server/clients/admin/rest.ts", + "syntaxKind": "MethodDeclaration", + "name": "put", + "value": "(params: PostRequestParams) => Promise", + "description": "Performs a PUT request on the given path." + }, + { + "filePath": "src/server/clients/admin/rest.ts", + "syntaxKind": "MethodDeclaration", + "name": "delete", + "value": "(params: GetRequestParams) => Promise", + "description": "Performs a DELETE request on the given path." + } + ], + "value": "class RemixRestClient {\n public session: Session;\n private params: AdminClientOptions['params'];\n private handleClientError: AdminClientOptions['handleClientError'];\n\n constructor({params, session, handleClientError}: AdminClientOptions) {\n this.params = params;\n this.handleClientError = handleClientError;\n this.session = session;\n }\n\n /**\n * Performs a GET request on the given path.\n */\n public async get(params: GetRequestParams) {\n return this.makeRequest({\n method: 'GET' as RequestParams['method'],\n ...params,\n });\n }\n\n /**\n * Performs a POST request on the given path.\n */\n public async post(params: PostRequestParams) {\n return this.makeRequest({\n method: 'POST' as RequestParams['method'],\n ...params,\n });\n }\n\n /**\n * Performs a PUT request on the given path.\n */\n public async put(params: PutRequestParams) {\n return this.makeRequest({\n method: 'PUT' as RequestParams['method'],\n ...params,\n });\n }\n\n /**\n * Performs a DELETE request on the given path.\n */\n public async delete(params: DeleteRequestParams) {\n return this.makeRequest({\n method: 'DELETE' as RequestParams['method'],\n ...params,\n });\n }\n\n protected async makeRequest(params: RequestParams): Promise {\n const originalClient = new this.params.api.clients.Rest({\n session: this.session,\n });\n const originalRequest = Reflect.get(originalClient, 'request');\n\n try {\n const apiResponse = await originalRequest.call(originalClient, params);\n\n // We use a separate client for REST requests and REST resources because we want to override the API library\n // client class to return a Response object instead.\n return new Response(JSON.stringify(apiResponse.body), {\n headers: apiResponse.headers,\n });\n } catch (error) {\n if (this.handleClientError) {\n throw await this.handleClientError({\n error,\n session: this.session,\n params: this.params,\n });\n } else throw new Error(error);\n }\n }\n}" + }, + "Session": { + "filePath": "../../node_modules/@shopify/shopify-api/lib/session/session.d.ts", + "name": "Session", + "description": "Stores App information from logged in merchants so they can make authenticated requests to the Admin API.", + "members": [ + { + "filePath": "../../node_modules/@shopify/shopify-api/lib/session/session.d.ts", + "syntaxKind": "PropertyDeclaration", + "name": "id", + "value": "string", + "description": "" + }, + { + "filePath": "../../node_modules/@shopify/shopify-api/lib/session/session.d.ts", + "syntaxKind": "PropertyDeclaration", + "name": "shop", + "value": "string", + "description": "" + }, + { + "filePath": "../../node_modules/@shopify/shopify-api/lib/session/session.d.ts", + "syntaxKind": "PropertyDeclaration", + "name": "state", + "value": "string", + "description": "" + }, + { + "filePath": "../../node_modules/@shopify/shopify-api/lib/session/session.d.ts", + "syntaxKind": "PropertyDeclaration", + "name": "isOnline", + "value": "boolean", + "description": "" + }, + { + "filePath": "../../node_modules/@shopify/shopify-api/lib/session/session.d.ts", + "syntaxKind": "PropertyDeclaration", + "name": "scope", + "value": "string", + "description": "" + }, + { + "filePath": "../../node_modules/@shopify/shopify-api/lib/session/session.d.ts", + "syntaxKind": "PropertyDeclaration", + "name": "expires", + "value": "Date", + "description": "" + }, + { + "filePath": "../../node_modules/@shopify/shopify-api/lib/session/session.d.ts", + "syntaxKind": "PropertyDeclaration", + "name": "accessToken", + "value": "string", + "description": "" + }, + { + "filePath": "../../node_modules/@shopify/shopify-api/lib/session/session.d.ts", + "syntaxKind": "PropertyDeclaration", + "name": "onlineAccessInfo", + "value": "OnlineAccessInfo", + "description": "" + }, + { + "filePath": "../../node_modules/@shopify/shopify-api/lib/session/session.d.ts", + "syntaxKind": "MethodDeclaration", + "name": "isActive", + "value": "(scopes: string | string[] | AuthScopes) => boolean", + "description": "" + }, + { + "filePath": "../../node_modules/@shopify/shopify-api/lib/session/session.d.ts", + "syntaxKind": "MethodDeclaration", + "name": "isScopeChanged", + "value": "(scopes: string | string[] | AuthScopes) => boolean", + "description": "" + }, + { + "filePath": "../../node_modules/@shopify/shopify-api/lib/session/session.d.ts", + "syntaxKind": "MethodDeclaration", + "name": "isExpired", + "value": "(withinMillisecondsOfExpiry?: number) => boolean", + "description": "" + }, + { + "filePath": "../../node_modules/@shopify/shopify-api/lib/session/session.d.ts", + "syntaxKind": "MethodDeclaration", + "name": "toObject", + "value": "() => SessionParams", + "description": "" + }, + { + "filePath": "../../node_modules/@shopify/shopify-api/lib/session/session.d.ts", + "syntaxKind": "MethodDeclaration", + "name": "equals", + "value": "(other: Session) => boolean", + "description": "" + }, + { + "filePath": "../../node_modules/@shopify/shopify-api/lib/session/session.d.ts", + "syntaxKind": "MethodDeclaration", + "name": "toPropertyArray", + "value": "() => [string, string | number | boolean][]", + "description": "" + } + ], + "value": "export declare class Session {\n static fromPropertyArray(entries: [string, string | number | boolean][]): Session;\n readonly id: string;\n shop: string;\n state: string;\n isOnline: boolean;\n scope?: string;\n expires?: Date;\n accessToken?: string;\n onlineAccessInfo?: OnlineAccessInfo;\n constructor(params: SessionParams);\n isActive(scopes: AuthScopes | string | string[]): boolean;\n isScopeChanged(scopes: AuthScopes | string | string[]): boolean;\n isExpired(withinMillisecondsOfExpiry?: number): boolean;\n toObject(): SessionParams;\n equals(other: Session | undefined): boolean;\n toPropertyArray(): [string, string | number | boolean][];\n}" + }, + "OnlineAccessInfo": { + "filePath": "../../node_modules/@shopify/shopify-api/lib/auth/oauth/types.d.ts", + "name": "OnlineAccessInfo", + "description": "", + "members": [ + { + "filePath": "../../node_modules/@shopify/shopify-api/lib/auth/oauth/types.d.ts", + "syntaxKind": "PropertySignature", + "name": "expires_in", + "value": "number", + "description": "" + }, + { + "filePath": "../../node_modules/@shopify/shopify-api/lib/auth/oauth/types.d.ts", + "syntaxKind": "PropertySignature", + "name": "associated_user_scope", + "value": "string", + "description": "" + }, + { + "filePath": "../../node_modules/@shopify/shopify-api/lib/auth/oauth/types.d.ts", + "syntaxKind": "PropertySignature", + "name": "associated_user", + "value": "{ id: number; first_name: string; last_name: string; email: string; email_verified: boolean; account_owner: boolean; locale: string; collaborator: boolean; }", + "description": "" + } + ], + "value": "export interface OnlineAccessInfo {\n expires_in: number;\n associated_user_scope: string;\n associated_user: {\n id: number;\n first_name: string;\n last_name: string;\n email: string;\n email_verified: boolean;\n account_owner: boolean;\n locale: string;\n collaborator: boolean;\n };\n}" + }, + "AuthScopes": { + "filePath": "../../node_modules/@shopify/shopify-api/lib/auth/scopes/index.d.ts", + "name": "AuthScopes", + "description": "", + "members": [ + { + "filePath": "../../node_modules/@shopify/shopify-api/lib/auth/scopes/index.d.ts", + "syntaxKind": "MethodDeclaration", + "name": "has", + "value": "(scope: string | string[] | AuthScopes) => boolean", + "description": "" + }, + { + "filePath": "../../node_modules/@shopify/shopify-api/lib/auth/scopes/index.d.ts", + "syntaxKind": "MethodDeclaration", + "name": "equals", + "value": "(otherScopes: string | string[] | AuthScopes) => boolean", + "description": "" + }, + { + "filePath": "../../node_modules/@shopify/shopify-api/lib/auth/scopes/index.d.ts", + "syntaxKind": "MethodDeclaration", + "name": "toString", + "value": "() => string", + "description": "" + }, + { + "filePath": "../../node_modules/@shopify/shopify-api/lib/auth/scopes/index.d.ts", + "syntaxKind": "MethodDeclaration", + "name": "toArray", + "value": "() => string[]", + "description": "" + } + ], + "value": "declare class AuthScopes {\n static SCOPE_DELIMITER: string;\n private compressedScopes;\n private expandedScopes;\n constructor(scopes: string | string[] | AuthScopes | undefined);\n has(scope: string | string[] | AuthScopes | undefined): boolean;\n equals(otherScopes: string | string[] | AuthScopes | undefined): boolean;\n toString(): string;\n toArray(): string[];\n private getImpliedScopes;\n}" + }, + "SessionParams": { + "filePath": "../../node_modules/@shopify/shopify-api/lib/session/types.d.ts", + "name": "SessionParams", + "description": "", + "members": [ + { + "filePath": "../../node_modules/@shopify/shopify-api/lib/session/types.d.ts", + "syntaxKind": "PropertySignature", + "name": "id", + "value": "string", + "description": "" + }, + { + "filePath": "../../node_modules/@shopify/shopify-api/lib/session/types.d.ts", + "syntaxKind": "PropertySignature", + "name": "shop", + "value": "string", + "description": "" + }, + { + "filePath": "../../node_modules/@shopify/shopify-api/lib/session/types.d.ts", + "syntaxKind": "PropertySignature", + "name": "state", + "value": "string", + "description": "" + }, + { + "filePath": "../../node_modules/@shopify/shopify-api/lib/session/types.d.ts", + "syntaxKind": "PropertySignature", + "name": "isOnline", + "value": "boolean", + "description": "" + }, + { + "filePath": "../../node_modules/@shopify/shopify-api/lib/session/types.d.ts", + "syntaxKind": "PropertySignature", + "name": "scope", + "value": "string", + "description": "", + "isOptional": true + }, + { + "filePath": "../../node_modules/@shopify/shopify-api/lib/session/types.d.ts", + "syntaxKind": "PropertySignature", + "name": "expires", + "value": "Date", + "description": "", + "isOptional": true + }, + { + "filePath": "../../node_modules/@shopify/shopify-api/lib/session/types.d.ts", + "syntaxKind": "PropertySignature", + "name": "accessToken", + "value": "string", + "description": "", + "isOptional": true + }, + { + "filePath": "../../node_modules/@shopify/shopify-api/lib/session/types.d.ts", + "syntaxKind": "PropertySignature", + "name": "onlineAccessInfo", + "value": "OnlineAccessInfo", + "description": "", + "isOptional": true + } + ], + "value": "export interface SessionParams {\n readonly id: string;\n shop: string;\n state: string;\n isOnline: boolean;\n scope?: string;\n expires?: Date;\n accessToken?: string;\n onlineAccessInfo?: OnlineAccessInfo;\n}" + }, + "GetRequestParams": { + "filePath": "../../node_modules/@shopify/shopify-api/lib/clients/types.d.ts", + "name": "GetRequestParams", + "description": "", + "members": [ + { + "filePath": "../../node_modules/@shopify/shopify-api/lib/clients/types.d.ts", + "syntaxKind": "PropertySignature", + "name": "path", + "value": "string", + "description": "" + }, + { + "filePath": "../../node_modules/@shopify/shopify-api/lib/clients/types.d.ts", + "syntaxKind": "PropertySignature", + "name": "type", + "value": "DataType", + "description": "", + "isOptional": true + }, + { + "filePath": "../../node_modules/@shopify/shopify-api/lib/clients/types.d.ts", + "syntaxKind": "PropertySignature", + "name": "data", + "value": "string | Record", + "description": "", + "isOptional": true + }, + { + "filePath": "../../node_modules/@shopify/shopify-api/lib/clients/types.d.ts", + "syntaxKind": "PropertySignature", + "name": "query", + "value": "SearchParams", + "description": "", + "isOptional": true + }, + { + "filePath": "../../node_modules/@shopify/shopify-api/lib/clients/types.d.ts", + "syntaxKind": "PropertySignature", + "name": "extraHeaders", + "value": "HeaderParams", + "description": "", + "isOptional": true + }, + { + "filePath": "../../node_modules/@shopify/shopify-api/lib/clients/types.d.ts", + "syntaxKind": "PropertySignature", + "name": "tries", + "value": "number", + "description": "", + "isOptional": true + } + ], + "value": "export interface GetRequestParams {\n path: string;\n type?: DataType;\n data?: Record | string;\n query?: SearchParams;\n extraHeaders?: HeaderParams;\n tries?: number;\n}" + }, + "DataType": { + "filePath": "../../node_modules/@shopify/shopify-api/lib/clients/types.d.ts", + "syntaxKind": "EnumDeclaration", + "name": "DataType", + "value": "export declare enum DataType {\n JSON = \"application/json\",\n GraphQL = \"application/graphql\",\n URLEncoded = \"application/x-www-form-urlencoded\"\n}", + "members": [ + { + "filePath": "../../node_modules/@shopify/shopify-api/lib/clients/types.d.ts", + "name": "JSON", + "value": "application/json" + }, + { + "filePath": "../../node_modules/@shopify/shopify-api/lib/clients/types.d.ts", + "name": "GraphQL", + "value": "application/graphql" + }, + { + "filePath": "../../node_modules/@shopify/shopify-api/lib/clients/types.d.ts", + "name": "URLEncoded", + "value": "application/x-www-form-urlencoded" + } + ] + }, + "HeaderParams": { + "filePath": "../../node_modules/@shopify/shopify-api/lib/clients/types.d.ts", + "syntaxKind": "TypeAliasDeclaration", + "name": "HeaderParams", + "value": "Record", + "description": "", + "members": [] + }, + "PostRequestParams": { + "filePath": "../../node_modules/@shopify/shopify-api/lib/clients/types.d.ts", + "syntaxKind": "TypeAliasDeclaration", + "name": "PostRequestParams", + "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<\n ResponseWithType>>\n>;" + }, + "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": "", + "isOptional": true + }, + { + "filePath": "src/server/clients/types.ts", + "syntaxKind": "PropertySignature", + "name": "apiVersion", + "value": "ApiVersion", + "description": "", + "isOptional": true + }, + { + "filePath": "src/server/clients/types.ts", + "syntaxKind": "PropertySignature", + "name": "headers", + "value": "{ [key: string]: any; }", + "description": "", + "isOptional": true + }, + { + "filePath": "src/server/clients/types.ts", + "syntaxKind": "PropertySignature", + "name": "tries", + "value": "number", + "description": "", + "isOptional": true + } + ], + "value": "export interface GraphQLQueryOptions<\n Operation extends keyof Operations,\n Operations extends AllOperations,\n> {\n variables?: ApiClientRequestOptions['variables'];\n apiVersion?: ApiVersion;\n headers?: {[key: string]: any};\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" + } + ] + } + } + } + ], + "jsDocTypeExamples": [ + "AdminApiContext" + ], + "related": [ + { + "name": "Authenticated context", + "subtitle": "Authenticate requests from Shopify Admin.", + "url": "/docs/api/shopify-app-remix/authenticate/admin" + }, + { + "name": "Unauthenticated context", + "subtitle": "Interact with the Admin API on non-Shopify requests.", + "url": "/docs/api/shopify-app-remix/unauthenticated/unauthenticated-admin" + } + ], + "examples": { + "description": "", + "exampleGroups": [ + { + "title": "rest", + "examples": [ + { + "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.", + "codeblock": { + "title": "Using REST resources", + "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};", + "language": "typescript" + }, + { + "title": "/app/shopify.server.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;", + "language": "typescript" + } + ] + } + }, + { + "description": "Use `admin.rest.get` to make custom requests to make a request to to the `customer/count` endpoint", + "codeblock": { + "title": "Performing a GET request to the REST API", + "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};", + "language": "typescript" + }, + { + "title": "/app/shopify.server.ts", + "code": "import { 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;", + "language": "typescript" + } + ] + } + }, + { + "description": "Use `admin.rest.post` to make custom requests to make a request to to the `customers.json` endpoint to send a welcome email", + "codeblock": { + "title": "Performing a POST request to the REST API", + "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};", + "language": "typescript" + }, + { + "title": "/app/shopify.server.ts", + "code": "import { 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;", + "language": "typescript" + } + ] + } + } + ] + }, + { + "title": "graphql", + "examples": [ + { + "description": "Use `admin.graphql` to make query / mutation requests.", + "codeblock": { + "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}", + "language": "typescript" + } + ] + } + } + ] + } + ] + } + }, + { + "name": "Storefront API", + "description": "Contains objects used to interact with the Storefront API.\n\nThis object is returned as part of different contexts, such as [`appProxy`](/docs/api/shopify-app-remix/authenticate/public/app-proxy), and [`unauthenticated.storefront`](/docs/api/shopify-app-remix/unauthenticated/unauthenticated-storefront).", + "category": "APIs", + "type": "object", + "isVisualComponent": false, + "definitions": [ + { + "title": "storefront", + "description": "Provides utilities that apps can use to make requests to the Storefront API.", + "type": "StorefrontContext", + "typeDefinitions": { + "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}" + }, + "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<\n ResponseWithType>>\n>;" + }, + "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": "", + "isOptional": true + }, + { + "filePath": "src/server/clients/types.ts", + "syntaxKind": "PropertySignature", + "name": "apiVersion", + "value": "ApiVersion", + "description": "", + "isOptional": true + }, + { + "filePath": "src/server/clients/types.ts", + "syntaxKind": "PropertySignature", + "name": "headers", + "value": "{ [key: string]: any; }", + "description": "", + "isOptional": true + }, + { + "filePath": "src/server/clients/types.ts", + "syntaxKind": "PropertySignature", + "name": "tries", + "value": "number", + "description": "", + "isOptional": true + } + ], + "value": "export interface GraphQLQueryOptions<\n Operation extends keyof Operations,\n Operations extends AllOperations,\n> {\n variables?: ApiClientRequestOptions['variables'];\n apiVersion?: ApiVersion;\n headers?: {[key: string]: any};\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" + } + ] + } + } + } + ], + "jsDocTypeExamples": [ + "StorefrontContext" + ], + "related": [ + { + "name": "App proxy context", + "subtitle": "Authenticate requests from Shopify app proxies.", + "url": "/docs/api/shopify-app-remix/authenticate/public/app-proxy" + }, + { + "name": "Unauthenticated context", + "subtitle": "Interact with the Storefront API on non-Shopify requests.", + "url": "/docs/api/shopify-app-remix/unauthenticated/unauthenticated-storefront" + } + ], + "examples": { + "description": "", + "exampleGroups": [ + { + "title": "graphql", + "examples": [ + { + "description": "Use `storefront.graphql` to make query / mutation requests.", + "codeblock": { + "title": "Querying the GraphQL API", + "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}", + "language": "typescript" + } + ] + } + } + ] + } + ] + } + }, + { + "name": "shopifyApp", + "description": "Returns a set of functions that can be used by the app's backend to be able to respond to all Shopify requests.\n\nThe shape of the returned object changes depending on the value of `distribution`. If it is `AppDistribution.ShopifyAdmin`, then only `ShopifyAppBase` objects are returned, otherwise `ShopifyAppLogin` objects are included.", + "category": "Entrypoints", + "type": "function", + "isVisualComponent": false, + "definitions": [ + { + "title": "shopifyApp", + "description": "Function to create a new Shopify API object.", + "type": "ShopifyAppGeneratedType", + "typeDefinitions": { + "ShopifyAppGeneratedType": { + "filePath": "src/server/shopify-app.ts", + "name": "ShopifyAppGeneratedType", + "description": "Creates an object your app will use to interact with Shopify.", + "params": [ + { + "name": "appConfig", + "description": "Configuration options for your Shopify app, such as the scopes your app needs.", + "value": "Config extends AppConfigArg", + "filePath": "src/server/shopify-app.ts" + } + ], + "returns": { + "filePath": "src/server/shopify-app.ts", + "description": "`ShopifyApp` An object constructed using your appConfig. It has methods for interacting with Shopify.", + "name": "ShopifyApp>", + "value": "ShopifyApp>" + }, + "value": "export function shopifyApp<\n Config extends AppConfigArg,\n Resources extends ShopifyRestResources,\n Storage extends SessionStorage,\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 oauth = new AuthCodeFlowStrategy(params);\n const authStrategy = authStrategyFactory({\n ...params,\n strategy: oauth,\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 public: authenticatePublicFactory(params),\n webhook: authenticateWebhookFactory<\n Config['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", + "description": "", + "tabs": [ + { + "code": "import { shopifyApp } from \"@shopify/shopify-app-remix/server\";\n\nconst shopify = shopifyApp({\n apiKey: process.env.SHOPIFY_API_KEY!,\n apiSecretKey: process.env.SHOPIFY_API_SECRET!,\n scopes: process.env.SCOPES?.split(\",\")!,\n appUrl: process.env.SHOPIFY_APP_URL!,\n});\nexport default shopify;", + "title": "/shopify.server.ts" + } + ] + } + ] + }, + "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/config-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`" + }, + { + "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 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 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" + } + ] + } + ] + }, + { + "filePath": "src/server/config-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, + "examples": [ + { + "title": "Seeding your database custom 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 seedStoreData({session})\n }\n },\n // ...etc\n});", + "title": "Example" + } + ] + } + ] + }, + { + "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", + "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\"`", + "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});\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" + } + ] + } + ] + }, + { + "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": "", + "isOptional": true + }, + { + "filePath": "src/server/config-types.ts", + "syntaxKind": "PropertySignature", + "name": "apiSecretKey", + "value": "string", + "description": "" + }, + { + "filePath": "src/server/config-types.ts", + "syntaxKind": "PropertySignature", + "name": "scopes", + "value": "string[] | AuthScopes", + "description": "", + "isOptional": true + }, + { + "filePath": "src/server/config-types.ts", + "syntaxKind": "PropertySignature", + "name": "adminApiAccessToken", + "value": "string", + "description": "", + "isOptional": true + }, + { + "filePath": "src/server/config-types.ts", + "syntaxKind": "PropertySignature", + "name": "userAgentPrefix", + "value": "string", + "description": "", + "isOptional": true + }, + { + "filePath": "src/server/config-types.ts", + "syntaxKind": "PropertySignature", + "name": "privateAppStorefrontAccessToken", + "value": "string", + "description": "", + "isOptional": true + }, + { + "filePath": "src/server/config-types.ts", + "syntaxKind": "PropertySignature", + "name": "customShopDomains", + "value": "(string | RegExp)[]", + "description": "", + "isOptional": true + }, + { + "filePath": "src/server/config-types.ts", + "syntaxKind": "PropertySignature", + "name": "billing", + "value": "BillingConfig", + "description": "", + "isOptional": true + }, + { + "filePath": "src/server/config-types.ts", + "syntaxKind": "PropertySignature", + "name": "restResources", + "value": "Resources", + "description": "", + "isOptional": true + }, + { + "filePath": "src/server/config-types.ts", + "syntaxKind": "PropertySignature", + "name": "logger", + "value": "{ log?: LogFunction; level?: LogSeverity; httpRequests?: boolean; timestamps?: boolean; }", + "description": "", + "isOptional": true + } + ], + "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}" + }, + "WebhookConfig": { + "filePath": "src/server/config-types.ts", + "name": "WebhookConfig", + "description": "", + "members": [ + { + "filePath": "src/server/config-types.ts", + "name": "[key: string]", + "value": "WebhookHandler | WebhookHandler[]" + } + ], + "value": "export interface WebhookConfig {\n [key: string]: WebhookHandler | WebhookHandler[];\n}" + }, + "WebhookHandler": { + "filePath": "../../node_modules/@shopify/shopify-api/lib/webhooks/types.d.ts", + "syntaxKind": "TypeAliasDeclaration", + "name": "WebhookHandler", + "value": "HttpWebhookHandler | HttpWebhookHandlerWithCallback | EventBridgeWebhookHandler | PubSubWebhookHandler", + "description": "" + }, + "HttpWebhookHandler": { + "filePath": "../../node_modules/@shopify/shopify-api/lib/webhooks/types.d.ts", + "name": "HttpWebhookHandler", + "description": "", + "members": [ + { + "filePath": "../../node_modules/@shopify/shopify-api/lib/webhooks/types.d.ts", + "syntaxKind": "PropertySignature", + "name": "deliveryMethod", + "value": "DeliveryMethod.Http", + "description": "" + }, + { + "filePath": "../../node_modules/@shopify/shopify-api/lib/webhooks/types.d.ts", + "syntaxKind": "PropertySignature", + "name": "callbackUrl", + "value": "string", + "description": "" + }, + { + "filePath": "../../node_modules/@shopify/shopify-api/lib/webhooks/types.d.ts", + "syntaxKind": "PropertySignature", + "name": "id", + "value": "string", + "description": "", + "isOptional": true + }, + { + "filePath": "../../node_modules/@shopify/shopify-api/lib/webhooks/types.d.ts", + "syntaxKind": "PropertySignature", + "name": "includeFields", + "value": "string[]", + "description": "", + "isOptional": true + }, + { + "filePath": "../../node_modules/@shopify/shopify-api/lib/webhooks/types.d.ts", + "syntaxKind": "PropertySignature", + "name": "metafieldNamespaces", + "value": "string[]", + "description": "", + "isOptional": true + } + ], + "value": "export interface HttpWebhookHandler extends BaseWebhookHandler {\n deliveryMethod: DeliveryMethod.Http;\n callbackUrl: string;\n}" + }, + "DeliveryMethod": { + "filePath": "../../node_modules/@shopify/shopify-api/lib/webhooks/types.d.ts", + "syntaxKind": "EnumDeclaration", + "name": "DeliveryMethod", + "value": "export declare enum DeliveryMethod {\n Http = \"http\",\n EventBridge = \"eventbridge\",\n PubSub = \"pubsub\"\n}", + "members": [ + { + "filePath": "../../node_modules/@shopify/shopify-api/lib/webhooks/types.d.ts", + "name": "Http", + "value": "http" + }, + { + "filePath": "../../node_modules/@shopify/shopify-api/lib/webhooks/types.d.ts", + "name": "EventBridge", + "value": "eventbridge" + }, + { + "filePath": "../../node_modules/@shopify/shopify-api/lib/webhooks/types.d.ts", + "name": "PubSub", + "value": "pubsub" + } + ] + }, + "HttpWebhookHandlerWithCallback": { + "filePath": "../../node_modules/@shopify/shopify-api/lib/webhooks/types.d.ts", + "name": "HttpWebhookHandlerWithCallback", + "description": "", + "members": [ + { + "filePath": "../../node_modules/@shopify/shopify-api/lib/webhooks/types.d.ts", + "syntaxKind": "PropertySignature", + "name": "callback", + "value": "WebhookHandlerFunction", + "description": "" + }, + { + "filePath": "../../node_modules/@shopify/shopify-api/lib/webhooks/types.d.ts", + "syntaxKind": "PropertySignature", + "name": "deliveryMethod", + "value": "DeliveryMethod.Http", + "description": "" + }, + { + "filePath": "../../node_modules/@shopify/shopify-api/lib/webhooks/types.d.ts", + "syntaxKind": "PropertySignature", + "name": "callbackUrl", + "value": "string", + "description": "" + }, + { + "filePath": "../../node_modules/@shopify/shopify-api/lib/webhooks/types.d.ts", + "syntaxKind": "PropertySignature", + "name": "id", + "value": "string", + "description": "", + "isOptional": true + }, + { + "filePath": "../../node_modules/@shopify/shopify-api/lib/webhooks/types.d.ts", + "syntaxKind": "PropertySignature", + "name": "includeFields", + "value": "string[]", + "description": "", + "isOptional": true + }, + { + "filePath": "../../node_modules/@shopify/shopify-api/lib/webhooks/types.d.ts", + "syntaxKind": "PropertySignature", + "name": "metafieldNamespaces", + "value": "string[]", + "description": "", + "isOptional": true + } + ], + "value": "export interface HttpWebhookHandlerWithCallback extends HttpWebhookHandler {\n callback: WebhookHandlerFunction;\n}" + }, + "WebhookHandlerFunction": { + "filePath": "../../node_modules/@shopify/shopify-api/lib/webhooks/types.d.ts", + "name": "WebhookHandlerFunction", + "description": "", + "params": [ + { + "name": "topic", + "description": "", + "value": "string", + "filePath": "../../node_modules/@shopify/shopify-api/lib/webhooks/types.d.ts" + }, + { + "name": "shop_domain", + "description": "", + "value": "string", + "filePath": "../../node_modules/@shopify/shopify-api/lib/webhooks/types.d.ts" + }, + { + "name": "body", + "description": "", + "value": "string", + "filePath": "../../node_modules/@shopify/shopify-api/lib/webhooks/types.d.ts" + }, + { + "name": "webhookId", + "description": "", + "value": "string", + "filePath": "../../node_modules/@shopify/shopify-api/lib/webhooks/types.d.ts" + }, + { + "name": "apiVersion", + "description": "", + "value": "string", + "isOptional": true, + "filePath": "../../node_modules/@shopify/shopify-api/lib/webhooks/types.d.ts" + } + ], + "returns": { + "filePath": "../../node_modules/@shopify/shopify-api/lib/webhooks/types.d.ts", + "description": "", + "name": "Promise", + "value": "Promise" + }, + "value": "export type WebhookHandlerFunction = (topic: string, shop_domain: string, body: string, webhookId: string, apiVersion?: string) => Promise;" + }, + "EventBridgeWebhookHandler": { + "filePath": "../../node_modules/@shopify/shopify-api/lib/webhooks/types.d.ts", + "name": "EventBridgeWebhookHandler", + "description": "", + "members": [ + { + "filePath": "../../node_modules/@shopify/shopify-api/lib/webhooks/types.d.ts", + "syntaxKind": "PropertySignature", + "name": "deliveryMethod", + "value": "DeliveryMethod.EventBridge", + "description": "" + }, + { + "filePath": "../../node_modules/@shopify/shopify-api/lib/webhooks/types.d.ts", + "syntaxKind": "PropertySignature", + "name": "arn", + "value": "string", + "description": "" + }, + { + "filePath": "../../node_modules/@shopify/shopify-api/lib/webhooks/types.d.ts", + "syntaxKind": "PropertySignature", + "name": "id", + "value": "string", + "description": "", + "isOptional": true + }, + { + "filePath": "../../node_modules/@shopify/shopify-api/lib/webhooks/types.d.ts", + "syntaxKind": "PropertySignature", + "name": "includeFields", + "value": "string[]", + "description": "", + "isOptional": true + }, + { + "filePath": "../../node_modules/@shopify/shopify-api/lib/webhooks/types.d.ts", + "syntaxKind": "PropertySignature", + "name": "metafieldNamespaces", + "value": "string[]", + "description": "", + "isOptional": true + } + ], + "value": "export interface EventBridgeWebhookHandler extends BaseWebhookHandler {\n deliveryMethod: DeliveryMethod.EventBridge;\n arn: string;\n}" + }, + "PubSubWebhookHandler": { + "filePath": "../../node_modules/@shopify/shopify-api/lib/webhooks/types.d.ts", + "name": "PubSubWebhookHandler", + "description": "", + "members": [ + { + "filePath": "../../node_modules/@shopify/shopify-api/lib/webhooks/types.d.ts", + "syntaxKind": "PropertySignature", + "name": "deliveryMethod", + "value": "DeliveryMethod.PubSub", + "description": "" + }, + { + "filePath": "../../node_modules/@shopify/shopify-api/lib/webhooks/types.d.ts", + "syntaxKind": "PropertySignature", + "name": "pubSubProject", + "value": "string", + "description": "" + }, + { + "filePath": "../../node_modules/@shopify/shopify-api/lib/webhooks/types.d.ts", + "syntaxKind": "PropertySignature", + "name": "pubSubTopic", + "value": "string", + "description": "" + }, + { + "filePath": "../../node_modules/@shopify/shopify-api/lib/webhooks/types.d.ts", + "syntaxKind": "PropertySignature", + "name": "id", + "value": "string", + "description": "", + "isOptional": true + }, + { + "filePath": "../../node_modules/@shopify/shopify-api/lib/webhooks/types.d.ts", + "syntaxKind": "PropertySignature", + "name": "includeFields", + "value": "string[]", + "description": "", + "isOptional": true + }, + { + "filePath": "../../node_modules/@shopify/shopify-api/lib/webhooks/types.d.ts", + "syntaxKind": "PropertySignature", + "name": "metafieldNamespaces", + "value": "string[]", + "description": "", + "isOptional": true + } + ], + "value": "export interface PubSubWebhookHandler extends BaseWebhookHandler {\n deliveryMethod: DeliveryMethod.PubSub;\n pubSubProject: string;\n pubSubTopic: string;\n}" + }, + "HooksConfig": { + "filePath": "src/server/config-types.ts", + "name": "HooksConfig", + "description": "", + "members": [ + { + "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" + } + ] + } + ] + } + ], + "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}" + }, + "AfterAuthOptions": { + "filePath": "src/server/config-types.ts", + "name": "AfterAuthOptions", + "description": "", + "members": [ + { + "filePath": "src/server/config-types.ts", + "syntaxKind": "PropertySignature", + "name": "session", + "value": "Session", + "description": "" + }, + { + "filePath": "src/server/config-types.ts", + "syntaxKind": "PropertySignature", + "name": "admin", + "value": "AdminApiContext", + "description": "" + } + ], + "value": "export interface AfterAuthOptions<\n R extends ShopifyRestResources = ShopifyRestResources,\n> {\n session: Session;\n admin: AdminApiContext;\n}" + }, + "Session": { + "filePath": "../../node_modules/@shopify/shopify-api/lib/session/session.d.ts", + "name": "Session", + "description": "Stores App information from logged in merchants so they can make authenticated requests to the Admin API.", + "members": [ + { + "filePath": "../../node_modules/@shopify/shopify-api/lib/session/session.d.ts", + "syntaxKind": "PropertyDeclaration", + "name": "id", + "value": "string", + "description": "" + }, + { + "filePath": "../../node_modules/@shopify/shopify-api/lib/session/session.d.ts", + "syntaxKind": "PropertyDeclaration", + "name": "shop", + "value": "string", + "description": "" + }, + { + "filePath": "../../node_modules/@shopify/shopify-api/lib/session/session.d.ts", + "syntaxKind": "PropertyDeclaration", + "name": "state", + "value": "string", + "description": "" + }, + { + "filePath": "../../node_modules/@shopify/shopify-api/lib/session/session.d.ts", + "syntaxKind": "PropertyDeclaration", + "name": "isOnline", + "value": "boolean", + "description": "" + }, + { + "filePath": "../../node_modules/@shopify/shopify-api/lib/session/session.d.ts", + "syntaxKind": "PropertyDeclaration", + "name": "scope", + "value": "string", + "description": "" + }, + { + "filePath": "../../node_modules/@shopify/shopify-api/lib/session/session.d.ts", + "syntaxKind": "PropertyDeclaration", + "name": "expires", + "value": "Date", + "description": "" + }, + { + "filePath": "../../node_modules/@shopify/shopify-api/lib/session/session.d.ts", + "syntaxKind": "PropertyDeclaration", + "name": "accessToken", + "value": "string", + "description": "" + }, + { + "filePath": "../../node_modules/@shopify/shopify-api/lib/session/session.d.ts", + "syntaxKind": "PropertyDeclaration", + "name": "onlineAccessInfo", + "value": "OnlineAccessInfo", + "description": "" + }, + { + "filePath": "../../node_modules/@shopify/shopify-api/lib/session/session.d.ts", + "syntaxKind": "MethodDeclaration", + "name": "isActive", + "value": "(scopes: string | string[] | AuthScopes) => boolean", + "description": "" + }, + { + "filePath": "../../node_modules/@shopify/shopify-api/lib/session/session.d.ts", + "syntaxKind": "MethodDeclaration", + "name": "isScopeChanged", + "value": "(scopes: string | string[] | AuthScopes) => boolean", + "description": "" + }, + { + "filePath": "../../node_modules/@shopify/shopify-api/lib/session/session.d.ts", + "syntaxKind": "MethodDeclaration", + "name": "isExpired", + "value": "(withinMillisecondsOfExpiry?: number) => boolean", + "description": "" + }, + { + "filePath": "../../node_modules/@shopify/shopify-api/lib/session/session.d.ts", + "syntaxKind": "MethodDeclaration", + "name": "toObject", + "value": "() => SessionParams", + "description": "" + }, + { + "filePath": "../../node_modules/@shopify/shopify-api/lib/session/session.d.ts", + "syntaxKind": "MethodDeclaration", + "name": "equals", + "value": "(other: Session) => boolean", + "description": "" + }, + { + "filePath": "../../node_modules/@shopify/shopify-api/lib/session/session.d.ts", + "syntaxKind": "MethodDeclaration", + "name": "toPropertyArray", + "value": "() => [string, string | number | boolean][]", + "description": "" + } + ], + "value": "export declare class Session {\n static fromPropertyArray(entries: [string, string | number | boolean][]): Session;\n readonly id: string;\n shop: string;\n state: string;\n isOnline: boolean;\n scope?: string;\n expires?: Date;\n accessToken?: string;\n onlineAccessInfo?: OnlineAccessInfo;\n constructor(params: SessionParams);\n isActive(scopes: AuthScopes | string | string[]): boolean;\n isScopeChanged(scopes: AuthScopes | string | string[]): boolean;\n isExpired(withinMillisecondsOfExpiry?: number): boolean;\n toObject(): SessionParams;\n equals(other: Session | undefined): boolean;\n toPropertyArray(): [string, string | number | boolean][];\n}" + }, + "OnlineAccessInfo": { + "filePath": "../../node_modules/@shopify/shopify-api/lib/auth/oauth/types.d.ts", + "name": "OnlineAccessInfo", + "description": "", + "members": [ + { + "filePath": "../../node_modules/@shopify/shopify-api/lib/auth/oauth/types.d.ts", + "syntaxKind": "PropertySignature", + "name": "expires_in", + "value": "number", + "description": "" + }, + { + "filePath": "../../node_modules/@shopify/shopify-api/lib/auth/oauth/types.d.ts", + "syntaxKind": "PropertySignature", + "name": "associated_user_scope", + "value": "string", + "description": "" + }, + { + "filePath": "../../node_modules/@shopify/shopify-api/lib/auth/oauth/types.d.ts", + "syntaxKind": "PropertySignature", + "name": "associated_user", + "value": "{ id: number; first_name: string; last_name: string; email: string; email_verified: boolean; account_owner: boolean; locale: string; collaborator: boolean; }", + "description": "" + } + ], + "value": "export interface OnlineAccessInfo {\n expires_in: number;\n associated_user_scope: string;\n associated_user: {\n id: number;\n first_name: string;\n last_name: string;\n email: string;\n email_verified: boolean;\n account_owner: boolean;\n locale: string;\n collaborator: boolean;\n };\n}" + }, + "AuthScopes": { + "filePath": "../../node_modules/@shopify/shopify-api/lib/auth/scopes/index.d.ts", + "name": "AuthScopes", + "description": "", + "members": [ + { + "filePath": "../../node_modules/@shopify/shopify-api/lib/auth/scopes/index.d.ts", + "syntaxKind": "MethodDeclaration", + "name": "has", + "value": "(scope: string | string[] | AuthScopes) => boolean", + "description": "" + }, + { + "filePath": "../../node_modules/@shopify/shopify-api/lib/auth/scopes/index.d.ts", + "syntaxKind": "MethodDeclaration", + "name": "equals", + "value": "(otherScopes: string | string[] | AuthScopes) => boolean", + "description": "" + }, + { + "filePath": "../../node_modules/@shopify/shopify-api/lib/auth/scopes/index.d.ts", + "syntaxKind": "MethodDeclaration", + "name": "toString", + "value": "() => string", + "description": "" + }, + { + "filePath": "../../node_modules/@shopify/shopify-api/lib/auth/scopes/index.d.ts", + "syntaxKind": "MethodDeclaration", + "name": "toArray", + "value": "() => string[]", + "description": "" + } + ], + "value": "declare class AuthScopes {\n static SCOPE_DELIMITER: string;\n private compressedScopes;\n private expandedScopes;\n constructor(scopes: string | string[] | AuthScopes | undefined);\n has(scope: string | string[] | AuthScopes | undefined): boolean;\n equals(otherScopes: string | string[] | AuthScopes | undefined): boolean;\n toString(): string;\n toArray(): string[];\n private getImpliedScopes;\n}" + }, + "SessionParams": { + "filePath": "../../node_modules/@shopify/shopify-api/lib/session/types.d.ts", + "name": "SessionParams", + "description": "", + "members": [ + { + "filePath": "../../node_modules/@shopify/shopify-api/lib/session/types.d.ts", + "syntaxKind": "PropertySignature", + "name": "id", + "value": "string", + "description": "" + }, + { + "filePath": "../../node_modules/@shopify/shopify-api/lib/session/types.d.ts", + "syntaxKind": "PropertySignature", + "name": "shop", + "value": "string", + "description": "" + }, + { + "filePath": "../../node_modules/@shopify/shopify-api/lib/session/types.d.ts", + "syntaxKind": "PropertySignature", + "name": "state", + "value": "string", + "description": "" + }, + { + "filePath": "../../node_modules/@shopify/shopify-api/lib/session/types.d.ts", + "syntaxKind": "PropertySignature", + "name": "isOnline", + "value": "boolean", + "description": "" + }, + { + "filePath": "../../node_modules/@shopify/shopify-api/lib/session/types.d.ts", + "syntaxKind": "PropertySignature", + "name": "scope", + "value": "string", + "description": "", + "isOptional": true + }, + { + "filePath": "../../node_modules/@shopify/shopify-api/lib/session/types.d.ts", + "syntaxKind": "PropertySignature", + "name": "expires", + "value": "Date", + "description": "", + "isOptional": true + }, + { + "filePath": "../../node_modules/@shopify/shopify-api/lib/session/types.d.ts", + "syntaxKind": "PropertySignature", + "name": "accessToken", + "value": "string", + "description": "", + "isOptional": true + }, + { + "filePath": "../../node_modules/@shopify/shopify-api/lib/session/types.d.ts", + "syntaxKind": "PropertySignature", + "name": "onlineAccessInfo", + "value": "OnlineAccessInfo", + "description": "", + "isOptional": true + } + ], + "value": "export interface SessionParams {\n readonly id: string;\n shop: string;\n state: string;\n isOnline: boolean;\n scope?: string;\n expires?: Date;\n accessToken?: string;\n onlineAccessInfo?: OnlineAccessInfo;\n}" + }, + "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 { admin, session } = await authenticate.admin(request);\n return json(admin.rest.resources.Order.count({ session }));\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};", + "title": "/app/routes/**\\/*.ts" + }, + { + "code": "import { 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" + } + ] + }, + { + "title": "Performing a POST request to the REST API", + "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};", + "title": "/app/routes/**\\/*.ts" + }, + { + "code": "import { 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/clients/admin/types.ts", + "syntaxKind": "PropertySignature", + "name": "graphql", + "value": "GraphQLClient", + "description": "Methods for interacting with the Shopify Admin GraphQL API\n\n\n\n\n\n\n\n\n\n", + "examples": [ + { + "title": "Querying the GraphQL API", + "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" + } + ] + } + ] + } + ], + "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 graphql: GraphQLClient;\n}" + }, + "RestClientWithResources": { + "filePath": "src/server/clients/admin/rest.ts", + "syntaxKind": "TypeAliasDeclaration", + "name": "RestClientWithResources", + "value": "RemixRestClient & {resources: Resources}", + "description": "" + }, + "RemixRestClient": { + "filePath": "src/server/clients/admin/rest.ts", + "name": "RemixRestClient", + "description": "", + "members": [ + { + "filePath": "src/server/clients/admin/rest.ts", + "syntaxKind": "PropertyDeclaration", + "name": "session", + "value": "Session", + "description": "" + }, + { + "filePath": "src/server/clients/admin/rest.ts", + "syntaxKind": "MethodDeclaration", + "name": "get", + "value": "(params: GetRequestParams) => Promise", + "description": "Performs a GET request on the given path." + }, + { + "filePath": "src/server/clients/admin/rest.ts", + "syntaxKind": "MethodDeclaration", + "name": "post", + "value": "(params: PostRequestParams) => Promise", + "description": "Performs a POST request on the given path." + }, + { + "filePath": "src/server/clients/admin/rest.ts", + "syntaxKind": "MethodDeclaration", + "name": "put", + "value": "(params: PostRequestParams) => Promise", + "description": "Performs a PUT request on the given path." + }, + { + "filePath": "src/server/clients/admin/rest.ts", + "syntaxKind": "MethodDeclaration", + "name": "delete", + "value": "(params: GetRequestParams) => Promise", + "description": "Performs a DELETE request on the given path." + } + ], + "value": "class RemixRestClient {\n public session: Session;\n private params: AdminClientOptions['params'];\n private handleClientError: AdminClientOptions['handleClientError'];\n\n constructor({params, session, handleClientError}: AdminClientOptions) {\n this.params = params;\n this.handleClientError = handleClientError;\n this.session = session;\n }\n\n /**\n * Performs a GET request on the given path.\n */\n public async get(params: GetRequestParams) {\n return this.makeRequest({\n method: 'GET' as RequestParams['method'],\n ...params,\n });\n }\n\n /**\n * Performs a POST request on the given path.\n */\n public async post(params: PostRequestParams) {\n return this.makeRequest({\n method: 'POST' as RequestParams['method'],\n ...params,\n });\n }\n\n /**\n * Performs a PUT request on the given path.\n */\n public async put(params: PutRequestParams) {\n return this.makeRequest({\n method: 'PUT' as RequestParams['method'],\n ...params,\n });\n }\n\n /**\n * Performs a DELETE request on the given path.\n */\n public async delete(params: DeleteRequestParams) {\n return this.makeRequest({\n method: 'DELETE' as RequestParams['method'],\n ...params,\n });\n }\n\n protected async makeRequest(params: RequestParams): Promise {\n const originalClient = new this.params.api.clients.Rest({\n session: this.session,\n });\n const originalRequest = Reflect.get(originalClient, 'request');\n\n try {\n const apiResponse = await originalRequest.call(originalClient, params);\n\n // We use a separate client for REST requests and REST resources because we want to override the API library\n // client class to return a Response object instead.\n return new Response(JSON.stringify(apiResponse.body), {\n headers: apiResponse.headers,\n });\n } catch (error) {\n if (this.handleClientError) {\n throw await this.handleClientError({\n error,\n session: this.session,\n params: this.params,\n });\n } else throw new Error(error);\n }\n }\n}" + }, + "GetRequestParams": { + "filePath": "../../node_modules/@shopify/shopify-api/lib/clients/types.d.ts", + "name": "GetRequestParams", + "description": "", + "members": [ + { + "filePath": "../../node_modules/@shopify/shopify-api/lib/clients/types.d.ts", + "syntaxKind": "PropertySignature", + "name": "path", + "value": "string", + "description": "" + }, + { + "filePath": "../../node_modules/@shopify/shopify-api/lib/clients/types.d.ts", + "syntaxKind": "PropertySignature", + "name": "type", + "value": "DataType", + "description": "", + "isOptional": true + }, + { + "filePath": "../../node_modules/@shopify/shopify-api/lib/clients/types.d.ts", + "syntaxKind": "PropertySignature", + "name": "data", + "value": "string | Record", + "description": "", + "isOptional": true + }, + { + "filePath": "../../node_modules/@shopify/shopify-api/lib/clients/types.d.ts", + "syntaxKind": "PropertySignature", + "name": "query", + "value": "SearchParams", + "description": "", + "isOptional": true + }, + { + "filePath": "../../node_modules/@shopify/shopify-api/lib/clients/types.d.ts", + "syntaxKind": "PropertySignature", + "name": "extraHeaders", + "value": "HeaderParams", + "description": "", + "isOptional": true + }, + { + "filePath": "../../node_modules/@shopify/shopify-api/lib/clients/types.d.ts", + "syntaxKind": "PropertySignature", + "name": "tries", + "value": "number", + "description": "", + "isOptional": true + } + ], + "value": "export interface GetRequestParams {\n path: string;\n type?: DataType;\n data?: Record | string;\n query?: SearchParams;\n extraHeaders?: HeaderParams;\n tries?: number;\n}" + }, + "DataType": { + "filePath": "../../node_modules/@shopify/shopify-api/lib/clients/types.d.ts", + "syntaxKind": "EnumDeclaration", + "name": "DataType", + "value": "export declare enum DataType {\n JSON = \"application/json\",\n GraphQL = \"application/graphql\",\n URLEncoded = \"application/x-www-form-urlencoded\"\n}", + "members": [ + { + "filePath": "../../node_modules/@shopify/shopify-api/lib/clients/types.d.ts", + "name": "JSON", + "value": "application/json" + }, + { + "filePath": "../../node_modules/@shopify/shopify-api/lib/clients/types.d.ts", + "name": "GraphQL", + "value": "application/graphql" + }, + { + "filePath": "../../node_modules/@shopify/shopify-api/lib/clients/types.d.ts", + "name": "URLEncoded", + "value": "application/x-www-form-urlencoded" + } + ] + }, + "HeaderParams": { + "filePath": "../../node_modules/@shopify/shopify-api/lib/clients/types.d.ts", + "syntaxKind": "TypeAliasDeclaration", + "name": "HeaderParams", + "value": "Record", + "description": "", + "members": [] + }, + "PostRequestParams": { + "filePath": "../../node_modules/@shopify/shopify-api/lib/clients/types.d.ts", + "syntaxKind": "TypeAliasDeclaration", + "name": "PostRequestParams", + "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<\n ResponseWithType>>\n>;" + }, + "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": "", + "isOptional": true + }, + { + "filePath": "src/server/clients/types.ts", + "syntaxKind": "PropertySignature", + "name": "apiVersion", + "value": "ApiVersion", + "description": "", + "isOptional": true + }, + { + "filePath": "src/server/clients/types.ts", + "syntaxKind": "PropertySignature", + "name": "headers", + "value": "{ [key: string]: any; }", + "description": "", + "isOptional": true + }, + { + "filePath": "src/server/clients/types.ts", + "syntaxKind": "PropertySignature", + "name": "tries", + "value": "number", + "description": "", + "isOptional": true + } + ], + "value": "export interface GraphQLQueryOptions<\n Operation extends keyof Operations,\n Operations extends AllOperations,\n> {\n variables?: ApiClientRequestOptions['variables'];\n apiVersion?: ApiVersion;\n headers?: {[key: string]: any};\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" + } + ] + }, + "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/types.ts", + "name": "SingleMerchant", + "value": "single_merchant" + }, + { + "filePath": "src/server/types.ts", + "name": "ShopifyAdmin", + "value": "shopify_admin" + } + ] + }, + "BillingConfig": { + "filePath": "../../node_modules/@shopify/shopify-api/lib/billing/types.d.ts", + "syntaxKind": "TypeAliasDeclaration", + "name": "BillingConfig", + "value": "Record>", + "description": "", + "members": [] + }, + "LogFunction": { + "filePath": "../../node_modules/@shopify/shopify-api/lib/base-types.d.ts", + "name": "LogFunction", + "description": "", + "params": [ + { + "name": "severity", + "description": "", + "value": "LogSeverity", + "filePath": "../../node_modules/@shopify/shopify-api/lib/base-types.d.ts" + }, + { + "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/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 + } + ] + }, + "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`." + }, + "AdminApp": { + "filePath": "src/server/types.ts", + "syntaxKind": "TypeAliasDeclaration", + "name": "AdminApp", + "value": "ShopifyAppBase", + "description": "", + "members": [ + { + "filePath": "src/server/types.ts", + "syntaxKind": "PropertySignature", + "name": "sessionStorage", + "value": "SessionStorageType", + "description": "The `SessionStorage` instance you passed in as a config option.", + "examples": [ + { + "title": "Storing sessions with Prisma", + "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\";\nimport prisma from \"~/db.server\";\n\nconst shopify = shopifyApp({\n sesssionStorage: new PrismaSessionStorage(prisma),\n // ...etc\n})\n\n// shopify.sessionStorage is an instance of PrismaSessionStorage", + "title": "/app/shopify.server.ts" + } + ] + } + ] + }, + { + "filePath": "src/server/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", + "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/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" + } + ] + } + ] + } + ] + }, + "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": [ + { + "name": "request", + "description": "", + "value": "Request", + "filePath": "src/server/types.ts" + }, + { + "name": "headers", + "description": "", + "value": "Headers", + "filePath": "src/server/types.ts" + } + ], + "returns": { + "filePath": "src/server/types.ts", + "description": "", + "name": "void", + "value": "void" + }, + "value": "type AddDocumentResponseHeaders = (request: Request, headers: Headers) => void;" + }, + "Headers": { + "filePath": "../../node_modules/@shopify/shopify-api/runtime/http/types.d.ts", + "syntaxKind": "TypeAliasDeclaration", + "name": "Headers", + "value": "Record", + "description": "", + "members": [] + }, + "RegisterWebhooks": { + "filePath": "src/server/types.ts", + "name": "RegisterWebhooks", + "description": "", + "params": [ + { + "name": "options", + "description": "", + "value": "RegisterWebhooksOptions", + "filePath": "src/server/types.ts" + } + ], + "returns": { + "filePath": "src/server/types.ts", + "description": "", + "name": "Promise", + "value": "Promise" + }, + "value": "type RegisterWebhooks = (\n options: RegisterWebhooksOptions,\n) => Promise;" + }, + "RegisterWebhooksOptions": { + "filePath": "src/server/authenticate/webhooks/types.ts", + "name": "RegisterWebhooksOptions", + "description": "", + "members": [ + { + "filePath": "src/server/authenticate/webhooks/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.", + "examples": [ + { + "title": "Authenticating a request for an embedded 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;\nexport const authenticate = shopify.authenticate;", + "title": "/app/shopify.server.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" + } + ] + } + ] + }, + { + "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/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 * ```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 */\n admin: AuthenticateAdmin>;\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/types.ts", + "name": "AuthenticateAdmin", + "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 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 { 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.", + "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" + } + ] + } + ] + }, + { + "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 {}" + }, + "BillingContext": { + "filePath": "src/server/authenticate/admin/billing/types.ts", + "name": "BillingContext", + "description": "", + "members": [ + { + "filePath": "src/server/authenticate/admin/billing/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.", + "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": "Using 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" + } + ] + } + ] + }, + { + "filePath": "src/server/authenticate/admin/billing/types.ts", + "syntaxKind": "PropertySignature", + "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": "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 * Using 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 */\n require: (\n options: RequireBillingOptions,\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", + "name": "RequireBillingOptions", + "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", + "syntaxKind": "PropertySignature", + "name": "isTest", + "value": "boolean", + "description": "", + "isOptional": true + } + ], + "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}" + }, + "BillingCheckResponseObject": { + "filePath": "../../node_modules/@shopify/shopify-api/lib/billing/types.d.ts", + "name": "BillingCheckResponseObject", + "description": "", + "members": [ + { + "filePath": "../../node_modules/@shopify/shopify-api/lib/billing/types.d.ts", + "syntaxKind": "PropertySignature", + "name": "hasActivePayment", + "value": "boolean", + "description": "" + }, + { + "filePath": "../../node_modules/@shopify/shopify-api/lib/billing/types.d.ts", + "syntaxKind": "PropertySignature", + "name": "oneTimePurchases", + "value": "OneTimePurchase[]", + "description": "" + }, + { + "filePath": "../../node_modules/@shopify/shopify-api/lib/billing/types.d.ts", + "syntaxKind": "PropertySignature", + "name": "appSubscriptions", + "value": "AppSubscription[]", + "description": "" + } + ], + "value": "export interface BillingCheckResponseObject {\n hasActivePayment: boolean;\n oneTimePurchases: OneTimePurchase[];\n appSubscriptions: AppSubscription[];\n}" + }, + "OneTimePurchase": { + "filePath": "../../node_modules/@shopify/shopify-api/lib/billing/types.d.ts", + "name": "OneTimePurchase", + "description": "", + "members": [ + { + "filePath": "../../node_modules/@shopify/shopify-api/lib/billing/types.d.ts", + "syntaxKind": "PropertySignature", + "name": "id", + "value": "string", + "description": "" + }, + { + "filePath": "../../node_modules/@shopify/shopify-api/lib/billing/types.d.ts", + "syntaxKind": "PropertySignature", + "name": "name", + "value": "string", + "description": "" + }, + { + "filePath": "../../node_modules/@shopify/shopify-api/lib/billing/types.d.ts", + "syntaxKind": "PropertySignature", + "name": "test", + "value": "boolean", + "description": "" + }, + { + "filePath": "../../node_modules/@shopify/shopify-api/lib/billing/types.d.ts", + "syntaxKind": "PropertySignature", + "name": "status", + "value": "string", + "description": "" + } + ], + "value": "export interface OneTimePurchase {\n id: string;\n name: string;\n test: boolean;\n status: string;\n}" + }, + "AppSubscription": { + "filePath": "../../node_modules/@shopify/shopify-api/lib/billing/types.d.ts", + "name": "AppSubscription", + "description": "", + "members": [ + { + "filePath": "../../node_modules/@shopify/shopify-api/lib/billing/types.d.ts", + "syntaxKind": "PropertySignature", + "name": "id", + "value": "string", + "description": "" + }, + { + "filePath": "../../node_modules/@shopify/shopify-api/lib/billing/types.d.ts", + "syntaxKind": "PropertySignature", + "name": "name", + "value": "string", + "description": "" + }, + { + "filePath": "../../node_modules/@shopify/shopify-api/lib/billing/types.d.ts", + "syntaxKind": "PropertySignature", + "name": "test", + "value": "boolean", + "description": "" + } + ], + "value": "export interface AppSubscription {\n id: string;\n name: string;\n test: boolean;\n}" + }, + "RequestBillingOptions": { + "filePath": "src/server/authenticate/admin/billing/types.ts", + "name": "RequestBillingOptions", + "description": "", + "members": [ + { + "filePath": "src/server/authenticate/admin/billing/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." + }, + { + "filePath": "src/server/authenticate/admin/billing/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 + }, + { + "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", + "syntaxKind": "PropertySignature", + "name": "subscriptionId", + "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", + "examples": [ + { + "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\";\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" + } + ] + } + ] + }, + { + "filePath": "src/server/authenticate/admin/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`.", + "examples": [ + { + "title": "Redirecting to an app route", + "description": "Use the `redirect` helper to safely redirect between pages.", + "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" + } + ] + }, + { + "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 { 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/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 { 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.", + "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" + } + ] + } + ] + }, + { + "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 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": "" + }, + { + "filePath": "../../node_modules/@shopify/shopify-api/lib/session/types.d.ts", + "syntaxKind": "PropertySignature", + "name": "dest", + "value": "string", + "description": "" + }, + { + "filePath": "../../node_modules/@shopify/shopify-api/lib/session/types.d.ts", + "syntaxKind": "PropertySignature", + "name": "aud", + "value": "string", + "description": "" + }, + { + "filePath": "../../node_modules/@shopify/shopify-api/lib/session/types.d.ts", + "syntaxKind": "PropertySignature", + "name": "sub", + "value": "string", + "description": "" + }, + { + "filePath": "../../node_modules/@shopify/shopify-api/lib/session/types.d.ts", + "syntaxKind": "PropertySignature", + "name": "exp", + "value": "number", + "description": "" + }, + { + "filePath": "../../node_modules/@shopify/shopify-api/lib/session/types.d.ts", + "syntaxKind": "PropertySignature", + "name": "nbf", + "value": "number", + "description": "" + }, + { + "filePath": "../../node_modules/@shopify/shopify-api/lib/session/types.d.ts", + "syntaxKind": "PropertySignature", + "name": "iat", + "value": "number", + "description": "" + }, + { + "filePath": "../../node_modules/@shopify/shopify-api/lib/session/types.d.ts", + "syntaxKind": "PropertySignature", + "name": "jti", + "value": "string", + "description": "" + }, + { + "filePath": "../../node_modules/@shopify/shopify-api/lib/session/types.d.ts", + "syntaxKind": "PropertySignature", + "name": "sid", + "value": "string", + "description": "" + } + ], + "value": "export interface JwtPayload {\n iss: string;\n dest: string;\n aud: string;\n sub: string;\n exp: number;\n nbf: number;\n iat: number;\n jti: string;\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": "" + }, + "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.", + "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" + } + ] + } + ] + } + ], + "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": "", + "isOptional": true + } + ], + "value": "interface Options {\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.", + "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" + } + ] + } + ] + } + ], + "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}" + }, + "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" + } ] } ] } ], - "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 WebhookContextWithoutSession\n extends Context {\n session: undefined;\n admin: undefined;\n}" }, - "RedirectFunction": { - "filePath": "/server/authenticate/admin/helpers/redirect.ts", - "name": "RedirectFunction", + "JSONValue": { + "filePath": "src/server/types.ts", + "syntaxKind": "TypeAliasDeclaration", + "name": "JSONValue", + "value": "string | number | boolean | null | JSONObject | JSONArray", + "description": "" + }, + "JSONObject": { + "filePath": "src/server/types.ts", + "name": "JSONObject", "description": "", - "params": [ + "members": [ + { + "filePath": "src/server/types.ts", + "name": "[x: string]", + "value": "JSONValue" + } + ], + "value": "interface JSONObject {\n [x: string]: JSONValue;\n}" + }, + "JSONArray": { + "filePath": "src/server/types.ts", + "name": "JSONArray", + "description": "", + "members": [ + { + "filePath": "src/server/types.ts", + "syntaxKind": "PropertySignature", + "name": "length", + "value": "number", + "description": "Gets or sets the length of the array. This is a number one higher than the highest index in the array." + }, + { + "filePath": "src/server/types.ts", + "syntaxKind": "MethodSignature", + "name": "toString", + "value": "() => string", + "description": "Returns a string representation of an array." + }, + { + "filePath": "src/server/types.ts", + "syntaxKind": "MethodSignature", + "name": "toLocaleString", + "value": "() => string", + "description": "Returns a string representation of an array. The elements are converted to string using their toLocaleString methods." + }, + { + "filePath": "src/server/types.ts", + "syntaxKind": "MethodSignature", + "name": "pop", + "value": "() => JSONValue", + "description": "Removes the last element from an array and returns it. If the array is empty, undefined is returned and the array is not modified." + }, + { + "filePath": "src/server/types.ts", + "syntaxKind": "MethodSignature", + "name": "push", + "value": "(...items: JSONValue[]) => number", + "description": "Appends new elements to the end of an array, and returns the new length of the array." + }, + { + "filePath": "src/server/types.ts", + "syntaxKind": "MethodSignature", + "name": "concat", + "value": "{ (...items: ConcatArray[]): JSONValue[]; (...items: (JSONValue | ConcatArray)[]): JSONValue[]; }", + "description": "Combines two or more arrays. This method returns a new array without modifying any existing arrays." + }, + { + "filePath": "src/server/types.ts", + "syntaxKind": "MethodSignature", + "name": "join", + "value": "(separator?: string) => string", + "description": "Adds all the elements of an array into a string, separated by the specified separator string." + }, + { + "filePath": "src/server/types.ts", + "syntaxKind": "MethodSignature", + "name": "reverse", + "value": "() => JSONValue[]", + "description": "Reverses the elements in an array in place. This method mutates the array and returns a reference to the same array." + }, + { + "filePath": "src/server/types.ts", + "syntaxKind": "MethodSignature", + "name": "shift", + "value": "() => JSONValue", + "description": "Removes the first element from an array and returns it. If the array is empty, undefined is returned and the array is not modified." + }, + { + "filePath": "src/server/types.ts", + "syntaxKind": "MethodSignature", + "name": "slice", + "value": "(start?: number, end?: number) => JSONValue[]", + "description": "Returns a copy of a section of an array. For both start and end, a negative index can be used to indicate an offset from the end of the array. For example, -2 refers to the second to last element of the array." + }, + { + "filePath": "src/server/types.ts", + "syntaxKind": "MethodSignature", + "name": "sort", + "value": "(compareFn?: (a: JSONValue, b: JSONValue) => number) => JSONArray", + "description": "Sorts an array in place. This method mutates the array and returns a reference to the same array." + }, + { + "filePath": "src/server/types.ts", + "syntaxKind": "MethodSignature", + "name": "splice", + "value": "{ (start: number, deleteCount?: number): JSONValue[]; (start: number, deleteCount: number, ...items: JSONValue[]): JSONValue[]; }", + "description": "Removes elements from an array and, if necessary, inserts new elements in their place, returning the deleted elements." + }, + { + "filePath": "src/server/types.ts", + "syntaxKind": "MethodSignature", + "name": "unshift", + "value": "(...items: JSONValue[]) => number", + "description": "Inserts new elements at the start of an array, and returns the new length of the array." + }, + { + "filePath": "src/server/types.ts", + "syntaxKind": "MethodSignature", + "name": "indexOf", + "value": "(searchElement: JSONValue, fromIndex?: number) => number", + "description": "Returns the index of the first occurrence of a value in an array, or -1 if it is not present." + }, + { + "filePath": "src/server/types.ts", + "syntaxKind": "MethodSignature", + "name": "lastIndexOf", + "value": "(searchElement: JSONValue, fromIndex?: number) => number", + "description": "Returns the index of the last occurrence of a specified value in an array, or -1 if it is not present." + }, + { + "filePath": "src/server/types.ts", + "syntaxKind": "MethodSignature", + "name": "every", + "value": "{ (predicate: (value: JSONValue, index: number, array: JSONValue[]) => value is S, thisArg?: any): this is S[]; (predicate: (value: JSONValue, index: number, array: JSONValue[]) => unknown, thisArg?: any): boolean; }", + "description": "Determines whether all the members of an array satisfy the specified test." + }, + { + "filePath": "src/server/types.ts", + "syntaxKind": "MethodSignature", + "name": "some", + "value": "(predicate: (value: JSONValue, index: number, array: JSONValue[]) => unknown, thisArg?: any) => boolean", + "description": "Determines whether the specified callback function returns true for any element of an array." + }, + { + "filePath": "src/server/types.ts", + "syntaxKind": "MethodSignature", + "name": "forEach", + "value": "(callbackfn: (value: JSONValue, index: number, array: JSONValue[]) => void, thisArg?: any) => void", + "description": "Performs the specified action for each element in an array." + }, + { + "filePath": "src/server/types.ts", + "syntaxKind": "MethodSignature", + "name": "map", + "value": "(callbackfn: (value: JSONValue, index: number, array: JSONValue[]) => U, thisArg?: any) => U[]", + "description": "Calls a defined callback function on each element of an array, and returns an array that contains the results." + }, + { + "filePath": "src/server/types.ts", + "syntaxKind": "MethodSignature", + "name": "filter", + "value": "{ (predicate: (value: JSONValue, index: number, array: JSONValue[]) => value is S, thisArg?: any): S[]; (predicate: (value: JSONValue, index: number, array: JSONValue[]) => unknown, thisArg?: any): JSONValue[]; }", + "description": "Returns the elements of an array that meet the condition specified in a callback function." + }, + { + "filePath": "src/server/types.ts", + "syntaxKind": "MethodSignature", + "name": "reduce", + "value": "{ (callbackfn: (previousValue: JSONValue, currentValue: JSONValue, currentIndex: number, array: JSONValue[]) => JSONValue): JSONValue; (callbackfn: (previousValue: JSONValue, currentValue: JSONValue, currentIndex: number, array: JSONValue[]) => JSONValue, initialValue: JSONValue): JSONValue; (callbackfn: (previousValue: U, currentValue: JSONValue, currentIndex: number, array: JSONValue[]) => U, initialValue: U): U; }", + "description": "Calls the specified callback function for all the elements in an array. The return value of the callback function is the accumulated result, and is provided as an argument in the next call to the callback function." + }, + { + "filePath": "src/server/types.ts", + "syntaxKind": "MethodSignature", + "name": "reduceRight", + "value": "{ (callbackfn: (previousValue: JSONValue, currentValue: JSONValue, currentIndex: number, array: JSONValue[]) => JSONValue): JSONValue; (callbackfn: (previousValue: JSONValue, currentValue: JSONValue, currentIndex: number, array: JSONValue[]) => JSONValue, initialValue: JSONValue): JSONValue; (callbackfn: (previousValue: U, currentValue: JSONValue, currentIndex: number, array: JSONValue[]) => U, initialValue: U): U; }", + "description": "Calls the specified callback function for all the elements in an array, in descending order. The return value of the callback function is the accumulated result, and is provided as an argument in the next call to the callback function." + }, + { + "filePath": "src/server/types.ts", + "syntaxKind": "MethodSignature", + "name": "find", + "value": "{ (predicate: (value: JSONValue, index: number, obj: JSONValue[]) => value is S, thisArg?: any): S; (predicate: (value: JSONValue, index: number, obj: JSONValue[]) => unknown, thisArg?: any): JSONValue; }", + "description": "Returns the value of the first element in the array where predicate is true, and undefined otherwise." + }, + { + "filePath": "src/server/types.ts", + "syntaxKind": "MethodSignature", + "name": "findIndex", + "value": "(predicate: (value: JSONValue, index: number, obj: JSONValue[]) => unknown, thisArg?: any) => number", + "description": "Returns the index of the first element in the array where predicate is true, and -1 otherwise." + }, + { + "filePath": "src/server/types.ts", + "syntaxKind": "MethodSignature", + "name": "fill", + "value": "(value: JSONValue, start?: number, end?: number) => JSONArray", + "description": "Changes all array elements from `start` to `end` index to a static `value` and returns the modified array" + }, + { + "filePath": "src/server/types.ts", + "syntaxKind": "MethodSignature", + "name": "copyWithin", + "value": "(target: number, start: number, end?: number) => JSONArray", + "description": "Returns the this object after copying a section of the array identified by start and end to the same array starting at position target" + }, + { + "filePath": "src/server/types.ts", + "syntaxKind": "MethodSignature", + "name": "entries", + "value": "() => IterableIterator<[number, JSONValue]>", + "description": "Returns an iterable of key, value pairs for every entry in the array" + }, + { + "filePath": "src/server/types.ts", + "syntaxKind": "MethodSignature", + "name": "keys", + "value": "() => IterableIterator", + "description": "Returns an iterable of keys in the array" + }, + { + "filePath": "src/server/types.ts", + "syntaxKind": "MethodSignature", + "name": "values", + "value": "() => IterableIterator", + "description": "Returns an iterable of values in the array" + }, + { + "filePath": "src/server/types.ts", + "syntaxKind": "MethodSignature", + "name": "includes", + "value": "(searchElement: JSONValue, fromIndex?: number) => boolean", + "description": "Determines whether an array includes a certain element, returning true or false as appropriate." + }, + { + "filePath": "src/server/types.ts", + "syntaxKind": "MethodSignature", + "name": "flatMap", + "value": "(callback: (this: This, value: JSONValue, index: number, array: JSONValue[]) => U | readonly U[], thisArg?: This) => U[]", + "description": "Calls a defined callback function on each element of an array. Then, flattens the result into a new array. This is identical to a map followed by flat with depth 1." + }, + { + "filePath": "src/server/types.ts", + "syntaxKind": "MethodSignature", + "name": "flat", + "value": "(this: A, depth?: D) => FlatArray[]", + "description": "Returns a new array with all sub-array elements concatenated into it recursively up to the specified depth." + }, + { + "filePath": "src/server/types.ts", + "syntaxKind": "MethodSignature", + "name": "__@iterator@1657", + "value": "() => IterableIterator", + "description": "Iterator" + }, { - "name": "url", - "description": "", - "value": "string", - "filePath": "/server/authenticate/admin/helpers/redirect.ts" + "filePath": "src/server/types.ts", + "syntaxKind": "PropertySignature", + "name": "__@unscopables@1659", + "value": "{ [x: number]: boolean; length?: boolean; toString?: boolean; toLocaleString?: boolean; pop?: boolean; push?: boolean; concat?: boolean; join?: boolean; reverse?: boolean; shift?: boolean; slice?: boolean; sort?: boolean; splice?: boolean; unshift?: boolean; indexOf?: boolean; lastIndexOf?: boolean; every?: boolean; some?: boolean; forEach?: boolean; map?: boolean; filter?: boolean; reduce?: boolean; reduceRight?: boolean; find?: boolean; findIndex?: boolean; fill?: boolean; copyWithin?: boolean; entries?: boolean; keys?: boolean; values?: boolean; includes?: boolean; flatMap?: boolean; flat?: boolean; [Symbol.iterator]?: boolean; readonly [Symbol.unscopables]?: boolean; at?: boolean; }", + "description": "Is an object whose properties have the value 'true' when they will be absent when used in a 'with' statement." }, { - "name": "init", - "description": "", - "value": "RedirectInit", - "isOptional": true, - "filePath": "/server/authenticate/admin/helpers/redirect.ts" + "filePath": "src/server/types.ts", + "syntaxKind": "MethodSignature", + "name": "at", + "value": "(index: number) => JSONValue", + "description": "Takes an integer value and returns the item at that index, allowing for positive and negative integers. Negative integers count back from the last item in the array." } ], - "returns": { - "filePath": "/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": "/server/authenticate/admin/helpers/redirect.ts", - "syntaxKind": "TypeAliasDeclaration", - "name": "RedirectInit", - "value": "number | (ResponseInit & {target?: RedirectTarget})", - "description": "" - }, - "RedirectTarget": { - "filePath": "/server/authenticate/admin/helpers/redirect.ts", - "syntaxKind": "TypeAliasDeclaration", - "name": "RedirectTarget", - "value": "'_self' | '_parent' | '_top'", - "description": "" + "value": "interface JSONArray extends Array {}" }, - "LegacyWebhookAdminApiContext": { - "filePath": "/server/authenticate/webhooks/types.ts", - "name": "LegacyWebhookAdminApiContext", + "WebhookContextWithSession": { + "filePath": "src/server/authenticate/webhooks/types.ts", + "name": "WebhookContextWithSession", "description": "", "members": [ { - "filePath": "/server/authenticate/webhooks/types.ts", + "filePath": "src/server/authenticate/webhooks/types.ts", "syntaxKind": "PropertySignature", - "name": "rest", - "value": "RestClient & Resources", - "description": "A REST client." + "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." }, { - "filePath": "/server/authenticate/webhooks/types.ts", + "filePath": "src/server/authenticate/webhooks/types.ts", "syntaxKind": "PropertySignature", - "name": "graphql", - "value": "InstanceType", - "description": "A GraphQL client." - } - ], - "value": "export interface LegacyWebhookAdminApiContext<\n Resources extends ShopifyRestResources,\n> {\n /** A REST client. */\n rest: InstanceType & Resources;\n /** A GraphQL client. */\n graphql: InstanceType;\n}" - } - } - } - ], - "jsDocTypeExamples": [ - "WebhookContextWithSession" - ], - "related": [ - { - "name": "Admin API context", - "subtitle": "Interact with the Admin API.", - "url": "/docs/api/shopify-app-remix/apis/admin-api" - } - ], - "examples": { - "description": "", - "exampleGroups": [ - { - "title": "admin", - "examples": [ - { - "description": "With the `v3_webhookAdminContext` future flag enabled, use the `admin` object in the context to interact with the Admin API.", - "codeblock": { - "title": "[V3] Webhook admin context", - "tabs": [ - { - "title": "/app/routes/webhooks.tsx", - "code": "import { ActionFunctionArgs } from \"@remix-run/node\";\nimport { authenticate } from \"../shopify.server\";\n\nexport async function action({ request }: ActionFunctionArgs) {\n const { admin } = await authenticate.webhook(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}", - "language": "typescript" - } - ] - } - }, - { - "description": "Use the `admin` object in the context to interact with the Admin API. This format will be removed in V3 of the package.", - "codeblock": { - "title": "Webhook admin context", - "tabs": [ - { - "title": "/app/routes/webhooks.tsx", - "code": "import { json, ActionFunctionArgs } from \"@remix-run/node\";\nimport { authenticate } from \"../shopify.server\";\n\nexport async function action({ request }: ActionFunctionArgs) {\n const { admin } = await authenticate.webhook(request);\n\n const response = await admin?.graphql.query<any>({\n data: {\n query: `#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\n const productData = response?.body.data;\n return json({ data: productData.data });\n}", - "language": "typescript" - } - ] - } - } - ] - }, - { - "title": "apiVersion", - "examples": [ - { - "description": "Get the API version used for webhook request.", - "codeblock": { - "title": "Webhook API version", - "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};", - "language": "typescript" - } - ] - } - } - ] - }, - { - "title": "shop", - "examples": [ - { - "description": "Get the shop that triggered a webhook.", - "codeblock": { - "title": "Webhook shop", - "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};", - "language": "typescript" - } - ] - } - } - ] - }, - { - "title": "topic", - "examples": [ - { - "description": "Get the event topic for the webhook.", - "codeblock": { - "title": "Webhook topic", - "tabs": [ + "name": "admin", + "value": "WebhookAdminContext", + "description": "An admin context for the webhook.\n\nReturned only if there is a session for the shop.", + "examples": [ { - "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};", - "language": "typescript" - } - ] - } - } - ] - }, - { - "title": "webhookId", - "examples": [ - { - "description": "Get the webhook ID.", - "codeblock": { - "title": "Webhook ID", - "tabs": [ + "title": "[V3] Webhook admin context", + "description": "With the `v3_webhookAdminContext` future flag enabled, 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.webhook(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/webhooks.tsx" + } + ] + }, { - "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};", - "language": "typescript" + "title": "Webhook admin context", + "description": "Use the `admin` object in the context to interact with the Admin API. This format will be removed in V3 of the package.", + "tabs": [ + { + "code": "import { json, ActionFunctionArgs } from \"@remix-run/node\";\nimport { authenticate } from \"../shopify.server\";\n\nexport async function action({ request }: ActionFunctionArgs) {\n const { admin } = await authenticate.webhook(request);\n\n const response = await admin?.graphql.query({\n data: {\n query: `#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\n const productData = response?.body.data;\n return json({ data: productData.data });\n}", + "title": "/app/routes/webhooks.tsx" + } + ] } ] - } - } - ] - }, - { - "title": "payload", - "examples": [ - { - "description": "Get the request's POST payload.", - "codeblock": { - "title": "Webhook payload", - "tabs": [ + }, + { + "filePath": "src/server/authenticate/webhooks/types.ts", + "syntaxKind": "PropertySignature", + "name": "apiVersion", + "value": "string", + "description": "The API version used for the webhook.", + "examples": [ { - "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};", - "language": "typescript" + "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" + } + ] } ] - } - } - ] - } - ] - } - }, - { - "name": "Admin API", - "description": "Contains objects used to interact with the Admin API.\n\nThis object is returned as part of different contexts, such as [`admin`](/docs/api/shopify-app-remix/authenticate/admin), [`unauthenticated.admin`](/docs/api/shopify-app-remix/unauthenticated/unauthenticated-admin), and [`webhook`](/docs/api/shopify-app-remix/authenticate/webhook).", - "category": "APIs", - "type": "object", - "isVisualComponent": false, - "definitions": [ - { - "title": "admin", - "description": "Provides utilities that apps can use to make requests to the Admin API.", - "type": "AdminApiContext", - "typeDefinitions": { - "AdminApiContext": { - "filePath": "/server/clients/admin/types.ts", - "name": "AdminApiContext", - "description": "", - "members": [ + }, { - "filePath": "/server/clients/admin/types.ts", + "filePath": "src/server/authenticate/webhooks/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": "shop", + "value": "string", + "description": "The shop where the webhook was triggered.", "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": "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 { admin, session } = await authenticate.admin(request);\n return json(admin.rest.resources.Order.count({ session }));\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" + "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": "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", + "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 { 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};", - "title": "/app/routes/**\\/*.ts" - }, - { - "code": "import { 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 { 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": "Performing a POST request to the REST API", - "description": "Use `admin.rest.post` to make custom requests to make a request to to the `customers.json` endpoint to send a welcome email", + "title": "Webhook ID", + "description": "Get the webhook ID.", "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};", - "title": "/app/routes/**\\/*.ts" - }, - { - "code": "import { 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 { 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": "/server/clients/admin/types.ts", + "filePath": "src/server/authenticate/webhooks/types.ts", "syntaxKind": "PropertySignature", - "name": "graphql", - "value": "GraphQLClient", - "description": "Methods for interacting with the Shopify Admin GraphQL API\n\n\n\n\n\n\n\n\n\n", + "name": "payload", + "value": "JSONValue", + "description": "The payload from the webhook request.", "examples": [ { - "title": "Querying the GraphQL API", - "description": "Use `admin.graphql` to make query / mutation requests.", + "title": "Webhook payload", + "description": "Get the request's POST payload.", "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 { 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" } ] } ] } ], - "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 graphql: GraphQLClient;\n}" + "value": "export interface WebhookContextWithSession<\n Future extends FutureFlagOptions,\n Resources extends ShopifyRestResources,\n Topics = string | number | symbol,\n> extends Context {\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 session: Session;\n\n /**\n * An admin context for the webhook.\n *\n * Returned only if there is a session for the shop.\n *\n * @example\n * [V3] Webhook admin context.\n * With the `v3_webhookAdminContext` future flag enabled, use the `admin` object in the context to interact with the Admin API.\n * ```ts\n * // /app/routes/webhooks.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.webhook(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 * @example\n * Webhook admin context.\n * Use the `admin` object in the context to interact with the Admin API. This format will be removed in V3 of the package.\n * ```ts\n * // /app/routes/webhooks.tsx\n * import { json, ActionFunctionArgs } from \"@remix-run/node\";\n * import { authenticate } from \"../shopify.server\";\n *\n * export async function action({ request }: ActionFunctionArgs) {\n * const { admin } = await authenticate.webhook(request);\n *\n * const response = await admin?.graphql.query({\n * data: {\n * query: `#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 *\n * const productData = response?.body.data;\n * return json({ data: productData.data });\n * }\n * ```\n */\n admin: WebhookAdminContext;\n}" }, - "RestClientWithResources": { - "filePath": "/server/clients/admin/rest.ts", + "WebhookAdminContext": { + "filePath": "src/server/authenticate/webhooks/types.ts", "syntaxKind": "TypeAliasDeclaration", - "name": "RestClientWithResources", - "value": "RemixRestClient & {resources: Resources}", + "name": "WebhookAdminContext", + "value": "FeatureEnabled extends true\n ? AdminApiContext\n : LegacyWebhookAdminApiContext", "description": "" - } - } - } - ], - "jsDocTypeExamples": [ - "AdminApiContext" - ], - "related": [ - { - "name": "Authenticated context", - "subtitle": "Authenticate requests from Shopify Admin.", - "url": "/docs/api/shopify-app-remix/authenticate/admin" - }, - { - "name": "Unauthenticated context", - "subtitle": "Interact with the Admin API on non-Shopify requests.", - "url": "/docs/api/shopify-app-remix/unauthenticated/unauthenticated-admin" - } - ], - "examples": { - "description": "", - "exampleGroups": [ - { - "title": "rest", - "examples": [ - { - "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.", - "codeblock": { - "title": "Using REST resources", - "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};", - "language": "typescript" - }, - { - "title": "/app/shopify.server.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;", - "language": "typescript" - } - ] - } - }, - { - "description": "Use `admin.rest.get` to make custom requests to make a request to to the `customer/count` endpoint", - "codeblock": { - "title": "Performing a GET request to the REST API", - "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};", - "language": "typescript" - }, - { - "title": "/app/shopify.server.ts", - "code": "import { 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;", - "language": "typescript" - } - ] - } - }, - { - "description": "Use `admin.rest.post` to make custom requests to make a request to to the `customers.json` endpoint to send a welcome email", - "codeblock": { - "title": "Performing a POST request to the REST API", - "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};", - "language": "typescript" - }, - { - "title": "/app/shopify.server.ts", - "code": "import { 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;", - "language": "typescript" - } - ] - } - } - ] - }, - { - "title": "graphql", - "examples": [ - { - "description": "Use `admin.graphql` to make query / mutation requests.", - "codeblock": { - "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}", - "language": "typescript" - } - ] - } - } - ] - } - ] - } - }, - { - "name": "Storefront API", - "description": "Contains objects used to interact with the Storefront API.\n\nThis object is returned as part of different contexts, such as [`appProxy`](/docs/api/shopify-app-remix/authenticate/public/app-proxy), and [`unauthenticated.storefront`](/docs/api/shopify-app-remix/unauthenticated/unauthenticated-storefront).", - "category": "APIs", - "type": "object", - "isVisualComponent": false, - "definitions": [ - { - "title": "storefront", - "description": "Provides utilities that apps can use to make requests to the Storefront API.", - "type": "StorefrontContext", - "typeDefinitions": { - "StorefrontContext": { - "filePath": "/server/clients/storefront/types.ts", - "name": "StorefrontContext", + }, + "LegacyWebhookAdminApiContext": { + "filePath": "src/server/authenticate/webhooks/types.ts", + "name": "LegacyWebhookAdminApiContext", "description": "", "members": [ { - "filePath": "/server/clients/storefront/types.ts", + "filePath": "src/server/authenticate/webhooks/types.ts", + "syntaxKind": "PropertySignature", + "name": "rest", + "value": "RestClient & Resources", + "description": "A REST client." + }, + { + "filePath": "src/server/authenticate/webhooks/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": "InstanceType", + "description": "A GraphQL client." } ], - "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}" - } - } - } - ], - "jsDocTypeExamples": [ - "StorefrontContext" - ], - "related": [ - { - "name": "App proxy context", - "subtitle": "Authenticate requests from Shopify app proxies.", - "url": "/docs/api/shopify-app-remix/authenticate/public/app-proxy" - }, - { - "name": "Unauthenticated context", - "subtitle": "Interact with the Storefront API on non-Shopify requests.", - "url": "/docs/api/shopify-app-remix/unauthenticated/unauthenticated-storefront" - } - ], - "examples": { - "description": "", - "exampleGroups": [ - { - "title": "graphql", - "examples": [ - { - "description": "Use `storefront.graphql` to make query / mutation requests.", - "codeblock": { - "title": "Querying the GraphQL API", - "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}", - "language": "typescript" - } - ] + "value": "export interface LegacyWebhookAdminApiContext<\n Resources extends ShopifyRestResources,\n> {\n /** A REST client. */\n rest: InstanceType & Resources;\n /** A GraphQL client. */\n graphql: InstanceType;\n}" + }, + "RestClient": { + "filePath": "../../node_modules/@shopify/shopify-api/lib/clients/admin/rest/client.d.ts", + "name": "RestClient", + "description": "", + "members": [ + { + "filePath": "../../node_modules/@shopify/shopify-api/lib/clients/admin/rest/client.d.ts", + "syntaxKind": "PropertyDeclaration", + "name": "loggedDeprecations", + "value": "Record", + "description": "" + }, + { + "filePath": "../../node_modules/@shopify/shopify-api/lib/clients/admin/rest/client.d.ts", + "syntaxKind": "PropertyDeclaration", + "name": "client", + "value": "AdminRestApiClient", + "description": "" + }, + { + "filePath": "../../node_modules/@shopify/shopify-api/lib/clients/admin/rest/client.d.ts", + "syntaxKind": "PropertyDeclaration", + "name": "session", + "value": "Session", + "description": "" + }, + { + "filePath": "../../node_modules/@shopify/shopify-api/lib/clients/admin/rest/client.d.ts", + "syntaxKind": "PropertyDeclaration", + "name": "apiVersion", + "value": "ApiVersion", + "description": "" + }, + { + "filePath": "../../node_modules/@shopify/shopify-api/lib/clients/admin/rest/client.d.ts", + "syntaxKind": "MethodDeclaration", + "name": "get", + "value": "(params: GetRequestParams) => Promise>", + "description": "Performs a GET request on the given path." + }, + { + "filePath": "../../node_modules/@shopify/shopify-api/lib/clients/admin/rest/client.d.ts", + "syntaxKind": "MethodDeclaration", + "name": "post", + "value": "(params: PostRequestParams) => Promise>", + "description": "Performs a POST request on the given path." + }, + { + "filePath": "../../node_modules/@shopify/shopify-api/lib/clients/admin/rest/client.d.ts", + "syntaxKind": "MethodDeclaration", + "name": "put", + "value": "(params: PostRequestParams) => Promise>", + "description": "Performs a PUT request on the given path." + }, + { + "filePath": "../../node_modules/@shopify/shopify-api/lib/clients/admin/rest/client.d.ts", + "syntaxKind": "MethodDeclaration", + "name": "delete", + "value": "(params: GetRequestParams) => Promise>", + "description": "Performs a DELETE request on the given path." } - } - ] - } - ] - } - }, - { - "name": "shopifyApp", - "description": "Returns a set of functions that can be used by the app's backend to be able to respond to all Shopify requests.\n\nThe shape of the returned object changes depending on the value of `distribution`. If it is `AppDistribution.ShopifyAdmin`, then only `ShopifyAppBase` objects are returned, otherwise `ShopifyAppLogin` objects are included.", - "category": "Entrypoints", - "type": "function", - "isVisualComponent": false, - "definitions": [ - { - "title": "shopifyApp", - "description": "Function to create a new Shopify API object.", - "type": "ShopifyAppGeneratedType", - "typeDefinitions": { - "ShopifyAppGeneratedType": { - "filePath": "/server/shopify-app.ts", - "name": "ShopifyAppGeneratedType", - "description": "Creates an object your app will use to interact with Shopify.", - "params": [ + ], + "value": "export declare class RestClient {\n static config: ConfigInterface;\n static formatPaths: boolean;\n static LINK_HEADER_REGEXP: RegExp;\n static DEFAULT_LIMIT: string;\n static RETRY_WAIT_TIME: number;\n static readonly DEPRECATION_ALERT_DELAY = 300000;\n loggedDeprecations: Record;\n readonly client: AdminRestApiClient;\n readonly session: Session;\n readonly apiVersion: ApiVersion;\n constructor({ session, apiVersion }: RestClientParams);\n /**\n * Performs a GET request on the given path.\n */\n get(params: GetRequestParams): Promise>;\n /**\n * Performs a POST request on the given path.\n */\n post(params: PostRequestParams): Promise>;\n /**\n * Performs a PUT request on the given path.\n */\n put(params: PutRequestParams): Promise>;\n /**\n * Performs a DELETE request on the given path.\n */\n delete(params: DeleteRequestParams): Promise>;\n protected request(params: RequestParams): Promise>;\n private restClass;\n private buildRequestParams;\n private logDeprecations;\n}" + }, + "RestRequestReturn": { + "filePath": "../../node_modules/@shopify/shopify-api/lib/clients/admin/types.d.ts", + "name": "RestRequestReturn", + "description": "", + "members": [ { - "name": "appConfig", - "description": "Configuration options for your Shopify app, such as the scopes your app needs.", - "value": "Config extends AppConfigArg", - "filePath": "/server/shopify-app.ts" + "filePath": "../../node_modules/@shopify/shopify-api/lib/clients/admin/types.d.ts", + "syntaxKind": "PropertySignature", + "name": "body", + "value": "T", + "description": "" + }, + { + "filePath": "../../node_modules/@shopify/shopify-api/lib/clients/admin/types.d.ts", + "syntaxKind": "PropertySignature", + "name": "headers", + "value": "Headers", + "description": "" + }, + { + "filePath": "../../node_modules/@shopify/shopify-api/lib/clients/admin/types.d.ts", + "syntaxKind": "PropertySignature", + "name": "pageInfo", + "value": "PageInfo", + "description": "", + "isOptional": true } ], - "returns": { - "filePath": "/server/shopify-app.ts", - "description": "`ShopifyApp` An object constructed using your appConfig. It has methods for interacting with Shopify.", - "name": "ShopifyApp>", - "value": "ShopifyApp>" - }, - "value": "export function shopifyApp<\n Config extends AppConfigArg,\n Resources extends ShopifyRestResources,\n Storage extends SessionStorage,\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 oauth = new AuthCodeFlowStrategy(params);\n const authStrategy = authStrategyFactory({\n ...params,\n strategy: oauth,\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 public: authenticatePublicFactory(params),\n webhook: authenticateWebhookFactory<\n Config['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": [ + "value": "export interface RestRequestReturn {\n body: T;\n headers: Headers;\n pageInfo?: PageInfo;\n}" + }, + "PageInfo": { + "filePath": "../../node_modules/@shopify/shopify-api/lib/clients/admin/types.d.ts", + "name": "PageInfo", + "description": "", + "members": [ { - "title": "The minimum viable configuration", + "filePath": "../../node_modules/@shopify/shopify-api/lib/clients/admin/types.d.ts", + "syntaxKind": "PropertySignature", + "name": "limit", + "value": "string", + "description": "" + }, + { + "filePath": "../../node_modules/@shopify/shopify-api/lib/clients/admin/types.d.ts", + "syntaxKind": "PropertySignature", + "name": "fields", + "value": "string[]", "description": "", - "tabs": [ - { - "code": "import { shopifyApp } from \"@shopify/shopify-app-remix/server\";\n\nconst shopify = shopifyApp({\n apiKey: process.env.SHOPIFY_API_KEY!,\n apiSecretKey: process.env.SHOPIFY_API_SECRET!,\n scopes: process.env.SCOPES?.split(\",\")!,\n appUrl: process.env.SHOPIFY_APP_URL!,\n});\nexport default shopify;", - "title": "/shopify.server.ts" - } - ] + "isOptional": true + }, + { + "filePath": "../../node_modules/@shopify/shopify-api/lib/clients/admin/types.d.ts", + "syntaxKind": "PropertySignature", + "name": "previousPageUrl", + "value": "string", + "description": "", + "isOptional": true + }, + { + "filePath": "../../node_modules/@shopify/shopify-api/lib/clients/admin/types.d.ts", + "syntaxKind": "PropertySignature", + "name": "nextPageUrl", + "value": "string", + "description": "", + "isOptional": true + }, + { + "filePath": "../../node_modules/@shopify/shopify-api/lib/clients/admin/types.d.ts", + "syntaxKind": "PropertySignature", + "name": "prevPage", + "value": "PageInfoParams", + "description": "", + "isOptional": true + }, + { + "filePath": "../../node_modules/@shopify/shopify-api/lib/clients/admin/types.d.ts", + "syntaxKind": "PropertySignature", + "name": "nextPage", + "value": "PageInfoParams", + "description": "", + "isOptional": true } - ] + ], + "value": "export interface PageInfo {\n limit: string;\n fields?: string[];\n previousPageUrl?: string;\n nextPageUrl?: string;\n prevPage?: PageInfoParams;\n nextPage?: PageInfoParams;\n}" }, - "AppConfigArg": { - "filePath": "/server/config-types.ts", - "name": "AppConfigArg", + "PageInfoParams": { + "filePath": "../../node_modules/@shopify/shopify-api/lib/clients/admin/types.d.ts", + "name": "PageInfoParams", "description": "", "members": [ { - "filePath": "/server/config-types.ts", + "filePath": "../../node_modules/@shopify/shopify-api/lib/clients/admin/types.d.ts", "syntaxKind": "PropertySignature", - "name": "appUrl", + "name": "path", "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." + "description": "" }, { - "filePath": "/server/config-types.ts", + "filePath": "../../node_modules/@shopify/shopify-api/lib/clients/admin/types.d.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" - } - ] - } - ] + "name": "query", + "value": "SearchParams", + "description": "" + } + ], + "value": "export interface PageInfoParams {\n path: string;\n query: SearchParams;\n}" + }, + "Shopify": { + "filePath": "../../node_modules/@shopify/shopify-api/lib/index.d.ts", + "name": "Shopify", + "description": "", + "members": [ + { + "filePath": "../../node_modules/@shopify/shopify-api/lib/index.d.ts", + "syntaxKind": "PropertySignature", + "name": "config", + "value": "ConfigInterface", + "description": "" + }, + { + "filePath": "../../node_modules/@shopify/shopify-api/lib/index.d.ts", + "syntaxKind": "PropertySignature", + "name": "clients", + "value": "ShopifyClients", + "description": "" + }, + { + "filePath": "../../node_modules/@shopify/shopify-api/lib/index.d.ts", + "syntaxKind": "PropertySignature", + "name": "auth", + "value": "ShopifyAuth", + "description": "" + }, + { + "filePath": "../../node_modules/@shopify/shopify-api/lib/index.d.ts", + "syntaxKind": "PropertySignature", + "name": "session", + "value": "ShopifySession", + "description": "" + }, + { + "filePath": "../../node_modules/@shopify/shopify-api/lib/index.d.ts", + "syntaxKind": "PropertySignature", + "name": "utils", + "value": "ShopifyUtils", + "description": "" + }, + { + "filePath": "../../node_modules/@shopify/shopify-api/lib/index.d.ts", + "syntaxKind": "PropertySignature", + "name": "webhooks", + "value": "ShopifyWebhooks", + "description": "" + }, + { + "filePath": "../../node_modules/@shopify/shopify-api/lib/index.d.ts", + "syntaxKind": "PropertySignature", + "name": "billing", + "value": "ShopifyBilling", + "description": "" + }, + { + "filePath": "../../node_modules/@shopify/shopify-api/lib/index.d.ts", + "syntaxKind": "PropertySignature", + "name": "logger", + "value": "ShopifyLogger", + "description": "" + }, + { + "filePath": "../../node_modules/@shopify/shopify-api/lib/index.d.ts", + "syntaxKind": "PropertySignature", + "name": "rest", + "value": "Resources", + "description": "" + } + ], + "value": "export interface Shopify {\n config: ConfigInterface;\n clients: ShopifyClients;\n auth: ShopifyAuth;\n session: ShopifySession;\n utils: ShopifyUtils;\n webhooks: ShopifyWebhooks;\n billing: ShopifyBilling;\n logger: ShopifyLogger;\n rest: Resources;\n}" + }, + "ConfigInterface": { + "filePath": "../../node_modules/@shopify/shopify-api/lib/base-types.d.ts", + "syntaxKind": "TypeAliasDeclaration", + "name": "ConfigInterface", + "value": "Omit & {\n apiKey: string;\n hostScheme: 'http' | 'https';\n scopes: AuthScopes;\n isCustomStoreApp: boolean;\n billing?: BillingConfig;\n logger: {\n log: LogFunction;\n level: LogSeverity;\n httpRequests: boolean;\n timestamps: boolean;\n };\n future: FutureFlagOptions;\n}", + "description": "" + }, + "Key": { + "filePath": "../../node_modules/@shopify/polaris/build/ts/src/types.d.ts", + "syntaxKind": "EnumDeclaration", + "name": "Key", + "value": "export declare enum Key {\n Backspace = 8,\n Tab = 9,\n Enter = 13,\n Shift = 16,\n Ctrl = 17,\n Alt = 18,\n Pause = 19,\n CapsLock = 20,\n Escape = 27,\n Space = 32,\n PageUp = 33,\n PageDown = 34,\n End = 35,\n Home = 36,\n LeftArrow = 37,\n UpArrow = 38,\n RightArrow = 39,\n DownArrow = 40,\n Insert = 45,\n Delete = 46,\n Key0 = 48,\n Key1 = 49,\n Key2 = 50,\n Key3 = 51,\n Key4 = 52,\n Key5 = 53,\n Key6 = 54,\n Key7 = 55,\n Key8 = 56,\n Key9 = 57,\n KeyA = 65,\n KeyB = 66,\n KeyC = 67,\n KeyD = 68,\n KeyE = 69,\n KeyF = 70,\n KeyG = 71,\n KeyH = 72,\n KeyI = 73,\n KeyJ = 74,\n KeyK = 75,\n KeyL = 76,\n KeyM = 77,\n KeyN = 78,\n KeyO = 79,\n KeyP = 80,\n KeyQ = 81,\n KeyR = 82,\n KeyS = 83,\n KeyT = 84,\n KeyU = 85,\n KeyV = 86,\n KeyW = 87,\n KeyX = 88,\n KeyY = 89,\n KeyZ = 90,\n LeftMeta = 91,\n RightMeta = 92,\n Select = 93,\n Numpad0 = 96,\n Numpad1 = 97,\n Numpad2 = 98,\n Numpad3 = 99,\n Numpad4 = 100,\n Numpad5 = 101,\n Numpad6 = 102,\n Numpad7 = 103,\n Numpad8 = 104,\n Numpad9 = 105,\n Multiply = 106,\n Add = 107,\n Subtract = 109,\n Decimal = 110,\n Divide = 111,\n F1 = 112,\n F2 = 113,\n F3 = 114,\n F4 = 115,\n F5 = 116,\n F6 = 117,\n F7 = 118,\n F8 = 119,\n F9 = 120,\n F10 = 121,\n F11 = 122,\n F12 = 123,\n NumLock = 144,\n ScrollLock = 145,\n Semicolon = 186,\n Equals = 187,\n Comma = 188,\n Dash = 189,\n Period = 190,\n ForwardSlash = 191,\n GraveAccent = 192,\n OpenBracket = 219,\n BackSlash = 220,\n CloseBracket = 221,\n SingleQuote = 222\n}", + "members": [ + { + "filePath": "../../node_modules/@shopify/polaris/build/ts/src/types.d.ts", + "name": "Backspace", + "value": 8 + }, + { + "filePath": "../../node_modules/@shopify/polaris/build/ts/src/types.d.ts", + "name": "Tab", + "value": 9 + }, + { + "filePath": "../../node_modules/@shopify/polaris/build/ts/src/types.d.ts", + "name": "Enter", + "value": 13 + }, + { + "filePath": "../../node_modules/@shopify/polaris/build/ts/src/types.d.ts", + "name": "Shift", + "value": 16 + }, + { + "filePath": "../../node_modules/@shopify/polaris/build/ts/src/types.d.ts", + "name": "Ctrl", + "value": 17 + }, + { + "filePath": "../../node_modules/@shopify/polaris/build/ts/src/types.d.ts", + "name": "Alt", + "value": 18 + }, + { + "filePath": "../../node_modules/@shopify/polaris/build/ts/src/types.d.ts", + "name": "Pause", + "value": 19 + }, + { + "filePath": "../../node_modules/@shopify/polaris/build/ts/src/types.d.ts", + "name": "CapsLock", + "value": 20 + }, + { + "filePath": "../../node_modules/@shopify/polaris/build/ts/src/types.d.ts", + "name": "Escape", + "value": 27 + }, + { + "filePath": "../../node_modules/@shopify/polaris/build/ts/src/types.d.ts", + "name": "Space", + "value": 32 + }, + { + "filePath": "../../node_modules/@shopify/polaris/build/ts/src/types.d.ts", + "name": "PageUp", + "value": 33 + }, + { + "filePath": "../../node_modules/@shopify/polaris/build/ts/src/types.d.ts", + "name": "PageDown", + "value": 34 + }, + { + "filePath": "../../node_modules/@shopify/polaris/build/ts/src/types.d.ts", + "name": "End", + "value": 35 + }, + { + "filePath": "../../node_modules/@shopify/polaris/build/ts/src/types.d.ts", + "name": "Home", + "value": 36 + }, + { + "filePath": "../../node_modules/@shopify/polaris/build/ts/src/types.d.ts", + "name": "LeftArrow", + "value": 37 + }, + { + "filePath": "../../node_modules/@shopify/polaris/build/ts/src/types.d.ts", + "name": "UpArrow", + "value": 38 + }, + { + "filePath": "../../node_modules/@shopify/polaris/build/ts/src/types.d.ts", + "name": "RightArrow", + "value": 39 + }, + { + "filePath": "../../node_modules/@shopify/polaris/build/ts/src/types.d.ts", + "name": "DownArrow", + "value": 40 + }, + { + "filePath": "../../node_modules/@shopify/polaris/build/ts/src/types.d.ts", + "name": "Insert", + "value": 45 + }, + { + "filePath": "../../node_modules/@shopify/polaris/build/ts/src/types.d.ts", + "name": "Delete", + "value": 46 + }, + { + "filePath": "../../node_modules/@shopify/polaris/build/ts/src/types.d.ts", + "name": "Key0", + "value": 48 + }, + { + "filePath": "../../node_modules/@shopify/polaris/build/ts/src/types.d.ts", + "name": "Key1", + "value": 49 + }, + { + "filePath": "../../node_modules/@shopify/polaris/build/ts/src/types.d.ts", + "name": "Key2", + "value": 50 + }, + { + "filePath": "../../node_modules/@shopify/polaris/build/ts/src/types.d.ts", + "name": "Key3", + "value": 51 + }, + { + "filePath": "../../node_modules/@shopify/polaris/build/ts/src/types.d.ts", + "name": "Key4", + "value": 52 + }, + { + "filePath": "../../node_modules/@shopify/polaris/build/ts/src/types.d.ts", + "name": "Key5", + "value": 53 + }, + { + "filePath": "../../node_modules/@shopify/polaris/build/ts/src/types.d.ts", + "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": "/server/config-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`" + "filePath": "../../node_modules/@shopify/polaris/build/ts/src/types.d.ts", + "name": "KeyK", + "value": 75 }, { - "filePath": "/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 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 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" - } - ] - } - ] + "filePath": "../../node_modules/@shopify/polaris/build/ts/src/types.d.ts", + "name": "KeyL", + "value": 76 }, { - "filePath": "/server/config-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, - "examples": [ - { - "title": "Seeding your database custom 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 seedStoreData({session})\n }\n },\n // ...etc\n});", - "title": "Example" - } - ] - } - ] + "filePath": "../../node_modules/@shopify/polaris/build/ts/src/types.d.ts", + "name": "KeyM", + "value": 77 }, { - "filePath": "/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": "../../node_modules/@shopify/polaris/build/ts/src/types.d.ts", + "name": "KeyN", + "value": 78 }, { - "filePath": "/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": "../../node_modules/@shopify/polaris/build/ts/src/types.d.ts", + "name": "KeyO", + "value": 79 }, { - "filePath": "/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": "../../node_modules/@shopify/polaris/build/ts/src/types.d.ts", + "name": "KeyP", + "value": 80 }, { - "filePath": "/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\"`", - "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});\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" - } - ] - } - ] + "filePath": "../../node_modules/@shopify/polaris/build/ts/src/types.d.ts", + "name": "KeyQ", + "value": 81 }, { - "filePath": "/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": "../../node_modules/@shopify/polaris/build/ts/src/types.d.ts", + "name": "KeyR", + "value": 82 }, { - "filePath": "/server/config-types.ts", - "syntaxKind": "PropertySignature", - "name": "apiKey", - "value": "string", - "description": "", - "isOptional": true + "filePath": "../../node_modules/@shopify/polaris/build/ts/src/types.d.ts", + "name": "KeyS", + "value": 83 }, { - "filePath": "/server/config-types.ts", - "syntaxKind": "PropertySignature", - "name": "apiSecretKey", - "value": "string", - "description": "" + "filePath": "../../node_modules/@shopify/polaris/build/ts/src/types.d.ts", + "name": "KeyT", + "value": 84 }, { - "filePath": "/server/config-types.ts", - "syntaxKind": "PropertySignature", - "name": "scopes", - "value": "string[] | AuthScopes", - "description": "", - "isOptional": true + "filePath": "../../node_modules/@shopify/polaris/build/ts/src/types.d.ts", + "name": "KeyU", + "value": 85 }, { - "filePath": "/server/config-types.ts", - "syntaxKind": "PropertySignature", - "name": "adminApiAccessToken", - "value": "string", - "description": "", - "isOptional": true + "filePath": "../../node_modules/@shopify/polaris/build/ts/src/types.d.ts", + "name": "KeyV", + "value": 86 }, { - "filePath": "/server/config-types.ts", - "syntaxKind": "PropertySignature", - "name": "userAgentPrefix", - "value": "string", - "description": "", - "isOptional": true + "filePath": "../../node_modules/@shopify/polaris/build/ts/src/types.d.ts", + "name": "KeyW", + "value": 87 }, { - "filePath": "/server/config-types.ts", - "syntaxKind": "PropertySignature", - "name": "privateAppStorefrontAccessToken", - "value": "string", - "description": "", - "isOptional": true + "filePath": "../../node_modules/@shopify/polaris/build/ts/src/types.d.ts", + "name": "KeyX", + "value": 88 }, { - "filePath": "/server/config-types.ts", - "syntaxKind": "PropertySignature", - "name": "customShopDomains", - "value": "(string | RegExp)[]", - "description": "", - "isOptional": true + "filePath": "../../node_modules/@shopify/polaris/build/ts/src/types.d.ts", + "name": "KeyY", + "value": 89 }, { - "filePath": "/server/config-types.ts", - "syntaxKind": "PropertySignature", - "name": "billing", - "value": "BillingConfig", - "description": "", - "isOptional": true + "filePath": "../../node_modules/@shopify/polaris/build/ts/src/types.d.ts", + "name": "KeyZ", + "value": 90 }, { - "filePath": "/server/config-types.ts", - "syntaxKind": "PropertySignature", - "name": "restResources", - "value": "Resources", - "description": "", - "isOptional": true + "filePath": "../../node_modules/@shopify/polaris/build/ts/src/types.d.ts", + "name": "LeftMeta", + "value": 91 }, { - "filePath": "/server/config-types.ts", - "syntaxKind": "PropertySignature", - "name": "logger", - "value": "{ log?: LogFunction; level?: LogSeverity; httpRequests?: boolean; timestamps?: boolean; }", - "description": "", - "isOptional": true - } - ], - "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}" - }, - "WebhookConfig": { - "filePath": "/server/config-types.ts", - "name": "WebhookConfig", - "description": "", - "members": [ + "filePath": "../../node_modules/@shopify/polaris/build/ts/src/types.d.ts", + "name": "RightMeta", + "value": 92 + }, { - "filePath": "/server/config-types.ts", - "name": "[key: string]", - "value": "WebhookHandler | WebhookHandler[]" - } - ], - "value": "export interface WebhookConfig {\n [key: string]: WebhookHandler | WebhookHandler[];\n}" - }, - "HooksConfig": { - "filePath": "/server/config-types.ts", - "name": "HooksConfig", - "description": "", - "members": [ + "filePath": "../../node_modules/@shopify/polaris/build/ts/src/types.d.ts", + "name": "Select", + "value": 93 + }, { - "filePath": "/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" - } - ] - } - ] - } - ], - "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}" - }, - "AfterAuthOptions": { - "filePath": "/server/config-types.ts", - "name": "AfterAuthOptions", - "description": "", - "members": [ + "filePath": "../../node_modules/@shopify/polaris/build/ts/src/types.d.ts", + "name": "Numpad0", + "value": 96 + }, { - "filePath": "/server/config-types.ts", - "syntaxKind": "PropertySignature", - "name": "session", - "value": "Session", - "description": "" + "filePath": "../../node_modules/@shopify/polaris/build/ts/src/types.d.ts", + "name": "Numpad1", + "value": 97 }, { - "filePath": "/server/config-types.ts", - "syntaxKind": "PropertySignature", - "name": "admin", - "value": "AdminApiContext", - "description": "" - } - ], - "value": "export interface AfterAuthOptions<\n R extends ShopifyRestResources = ShopifyRestResources,\n> {\n session: Session;\n admin: AdminApiContext;\n}" - }, - "AdminApiContext": { - "filePath": "/server/clients/admin/types.ts", - "name": "AdminApiContext", - "description": "", - "members": [ + "filePath": "../../node_modules/@shopify/polaris/build/ts/src/types.d.ts", + "name": "Numpad2", + "value": 98 + }, { - "filePath": "/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 { admin, session } = await authenticate.admin(request);\n return json(admin.rest.resources.Order.count({ session }));\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};", - "title": "/app/routes/**\\/*.ts" - }, - { - "code": "import { 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" - } - ] - }, - { - "title": "Performing a POST request to the REST API", - "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};", - "title": "/app/routes/**\\/*.ts" - }, - { - "code": "import { 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": "../../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": "/server/clients/admin/types.ts", - "syntaxKind": "PropertySignature", - "name": "graphql", - "value": "GraphQLClient", - "description": "Methods for interacting with the Shopify Admin GraphQL API\n\n\n\n\n\n\n\n\n\n", - "examples": [ - { - "title": "Querying the GraphQL API", - "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" - } - ] - } - ] - } - ], - "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 graphql: GraphQLClient;\n}" - }, - "RestClientWithResources": { - "filePath": "/server/clients/admin/rest.ts", - "syntaxKind": "TypeAliasDeclaration", - "name": "RestClientWithResources", - "value": "RemixRestClient & {resources: Resources}", - "description": "" - }, - "AppDistribution": { - "filePath": "/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": "../../node_modules/@shopify/polaris/build/ts/src/types.d.ts", + "name": "Numpad6", + "value": 102 + }, { - "filePath": "/server/types.ts", - "name": "AppStore", - "value": "app_store" + "filePath": "../../node_modules/@shopify/polaris/build/ts/src/types.d.ts", + "name": "Numpad7", + "value": 103 }, { - "filePath": "/server/types.ts", - "name": "SingleMerchant", - "value": "single_merchant" + "filePath": "../../node_modules/@shopify/polaris/build/ts/src/types.d.ts", + "name": "Numpad8", + "value": 104 }, { - "filePath": "/server/types.ts", - "name": "ShopifyAdmin", - "value": "shopify_admin" - } - ] - }, - "FutureFlags": { - "filePath": "/server/future/flags.ts", - "name": "FutureFlags", - "description": "", - "members": [ + "filePath": "../../node_modules/@shopify/polaris/build/ts/src/types.d.ts", + "name": "Numpad9", + "value": 105 + }, { - "filePath": "/server/future/flags.ts", - "syntaxKind": "PropertySignature", - "name": "v3_webhookAdminContext", - "value": "boolean", - "description": "When enabled, returns the same `admin` context (`AdminApiContext`) from `authenticate.webhook` that is returned from `authenticate.admin`.", - "isOptional": true, - "defaultValue": "false" + "filePath": "../../node_modules/@shopify/polaris/build/ts/src/types.d.ts", + "name": "Multiply", + "value": 106 }, { - "filePath": "/server/future/flags.ts", - "syntaxKind": "PropertySignature", - "name": "v3_authenticatePublic", - "value": "boolean", - "description": "When enabled authenticate.public() will not work. Use authenticate.public.checkout() instead.", - "isOptional": true, - "defaultValue": "false" - } - ], - "value": "export interface FutureFlags {\n /**\n * When enabled, returns the same `admin` context (`AdminApiContext`) from `authenticate.webhook` that is returned from `authenticate.admin`.\n *\n * @default false\n */\n v3_webhookAdminContext?: boolean;\n\n /**\n * When enabled authenticate.public() will not work. Use authenticate.public.checkout() instead.\n *\n * @default false\n */\n v3_authenticatePublic?: boolean;\n}" - }, - "ShopifyApp": { - "filePath": "/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`." - }, - "AdminApp": { - "filePath": "/server/types.ts", - "syntaxKind": "TypeAliasDeclaration", - "name": "AdminApp", - "value": "ShopifyAppBase", - "description": "", - "members": [ + "filePath": "../../node_modules/@shopify/polaris/build/ts/src/types.d.ts", + "name": "Add", + "value": 107 + }, { - "filePath": "/server/types.ts", - "syntaxKind": "PropertySignature", - "name": "sessionStorage", - "value": "SessionStorageType", - "description": "The `SessionStorage` instance you passed in as a config option.", - "examples": [ - { - "title": "Storing sessions with Prisma", - "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\";\nimport prisma from \"~/db.server\";\n\nconst shopify = shopifyApp({\n sesssionStorage: new PrismaSessionStorage(prisma),\n // ...etc\n})\n\n// shopify.sessionStorage is an instance of PrismaSessionStorage", - "title": "/app/shopify.server.ts" - } - ] - } - ] + "filePath": "../../node_modules/@shopify/polaris/build/ts/src/types.d.ts", + "name": "Subtract", + "value": 109 }, { - "filePath": "/server/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", - "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": "../../node_modules/@shopify/polaris/build/ts/src/types.d.ts", + "name": "Decimal", + "value": 110 }, { - "filePath": "/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": "../../node_modules/@shopify/polaris/build/ts/src/types.d.ts", + "name": "Divide", + "value": 111 }, { - "filePath": "/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": "../../node_modules/@shopify/polaris/build/ts/src/types.d.ts", + "name": "F1", + "value": 112 }, { - "filePath": "/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" - } - ] - } - ] - } - ] - }, - "SessionStorageType": { - "filePath": "/server/types.ts", - "syntaxKind": "TypeAliasDeclaration", - "name": "SessionStorageType", - "value": "Config['sessionStorage'] extends SessionStorage\n ? Config['sessionStorage']\n : SessionStorage", - "description": "" - }, - "AddDocumentResponseHeaders": { - "filePath": "/server/types.ts", - "name": "AddDocumentResponseHeaders", - "description": "", - "params": [ + "filePath": "../../node_modules/@shopify/polaris/build/ts/src/types.d.ts", + "name": "F2", + "value": 113 + }, { - "name": "request", - "description": "", - "value": "Request", - "filePath": "/server/types.ts" + "filePath": "../../node_modules/@shopify/polaris/build/ts/src/types.d.ts", + "name": "F3", + "value": 114 }, { - "name": "headers", - "description": "", - "value": "Headers", - "filePath": "/server/types.ts" - } - ], - "returns": { - "filePath": "/server/types.ts", - "description": "", - "name": "void", - "value": "void" - }, - "value": "type AddDocumentResponseHeaders = (request: Request, headers: Headers) => void;" - }, - "RegisterWebhooks": { - "filePath": "/server/types.ts", - "name": "RegisterWebhooks", - "description": "", - "params": [ + "filePath": "../../node_modules/@shopify/polaris/build/ts/src/types.d.ts", + "name": "F4", + "value": 115 + }, { - "name": "options", - "description": "", - "value": "RegisterWebhooksOptions", - "filePath": "/server/types.ts" - } - ], - "returns": { - "filePath": "/server/types.ts", - "description": "", - "name": "Promise", - "value": "Promise" - }, - "value": "type RegisterWebhooks = (\n options: RegisterWebhooksOptions,\n) => Promise;" - }, - "RegisterWebhooksOptions": { - "filePath": "/server/authenticate/webhooks/types.ts", - "name": "RegisterWebhooksOptions", - "description": "", - "members": [ + "filePath": "../../node_modules/@shopify/polaris/build/ts/src/types.d.ts", + "name": "F5", + "value": 116 + }, { - "filePath": "/server/authenticate/webhooks/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}" - }, - "Authenticate": { - "filePath": "/server/types.ts", - "name": "Authenticate", - "description": "", - "members": [ + "filePath": "../../node_modules/@shopify/polaris/build/ts/src/types.d.ts", + "name": "F6", + "value": 117 + }, { - "filePath": "/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.", - "examples": [ - { - "title": "Authenticating a request for an embedded 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;\nexport const authenticate = shopify.authenticate;", - "title": "/app/shopify.server.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" - } - ] - } - ] + "filePath": "../../node_modules/@shopify/polaris/build/ts/src/types.d.ts", + "name": "F7", + "value": 118 + }, + { + "filePath": "../../node_modules/@shopify/polaris/build/ts/src/types.d.ts", + "name": "F8", + "value": 119 + }, + { + "filePath": "../../node_modules/@shopify/polaris/build/ts/src/types.d.ts", + "name": "F9", + "value": 120 + }, + { + "filePath": "../../node_modules/@shopify/polaris/build/ts/src/types.d.ts", + "name": "F10", + "value": 121 + }, + { + "filePath": "../../node_modules/@shopify/polaris/build/ts/src/types.d.ts", + "name": "F11", + "value": 122 + }, + { + "filePath": "../../node_modules/@shopify/polaris/build/ts/src/types.d.ts", + "name": "F12", + "value": 123 + }, + { + "filePath": "../../node_modules/@shopify/polaris/build/ts/src/types.d.ts", + "name": "NumLock", + "value": 144 }, { - "filePath": "/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": "../../node_modules/@shopify/polaris/build/ts/src/types.d.ts", + "name": "ScrollLock", + "value": 145 }, { - "filePath": "/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/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 * ```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 */\n admin: AuthenticateAdmin>;\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": "/server/types.ts", - "name": "AuthenticateAdmin", - "description": "", - "params": [ + "filePath": "../../node_modules/@shopify/polaris/build/ts/src/types.d.ts", + "name": "Semicolon", + "value": 186 + }, { - "name": "request", - "description": "", - "value": "Request", - "filePath": "/server/types.ts" - } - ], - "returns": { - "filePath": "/server/types.ts", - "description": "", - "name": "Promise>", - "value": "Promise>" - }, - "value": "type AuthenticateAdmin<\n Config extends AppConfigArg,\n Resources extends ShopifyRestResources = ShopifyRestResources,\n> = (request: Request) => Promise>;" - }, - "AdminContext": { - "filePath": "/server/authenticate/admin/types.ts", - "syntaxKind": "TypeAliasDeclaration", - "name": "AdminContext", - "value": "Config['isEmbeddedApp'] extends false\n ? NonEmbeddedAdminContext\n : EmbeddedAdminContext", - "description": "" - }, - "NonEmbeddedAdminContext": { - "filePath": "/server/authenticate/admin/types.ts", - "name": "NonEmbeddedAdminContext", - "description": "", - "members": [ + "filePath": "../../node_modules/@shopify/polaris/build/ts/src/types.d.ts", + "name": "Equals", + "value": 187 + }, { - "filePath": "/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 { 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.", - "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" - } - ] - } - ] + "filePath": "../../node_modules/@shopify/polaris/build/ts/src/types.d.ts", + "name": "Comma", + "value": 188 }, { - "filePath": "/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": "../../node_modules/@shopify/polaris/build/ts/src/types.d.ts", + "name": "Dash", + "value": 189 }, { - "filePath": "/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": "../../node_modules/@shopify/polaris/build/ts/src/types.d.ts", + "name": "Period", + "value": 190 }, { - "filePath": "/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" - } - ] - } - ] + "filePath": "../../node_modules/@shopify/polaris/build/ts/src/types.d.ts", + "name": "ForwardSlash", + "value": 191 + }, + { + "filePath": "../../node_modules/@shopify/polaris/build/ts/src/types.d.ts", + "name": "GraveAccent", + "value": 192 + }, + { + "filePath": "../../node_modules/@shopify/polaris/build/ts/src/types.d.ts", + "name": "OpenBracket", + "value": 219 + }, + { + "filePath": "../../node_modules/@shopify/polaris/build/ts/src/types.d.ts", + "name": "BackSlash", + "value": 220 + }, + { + "filePath": "../../node_modules/@shopify/polaris/build/ts/src/types.d.ts", + "name": "CloseBracket", + "value": 221 + }, + { + "filePath": "../../node_modules/@shopify/polaris/build/ts/src/types.d.ts", + "name": "SingleQuote", + "value": 222 } - ], - "value": "export interface NonEmbeddedAdminContext<\n Config extends AppConfigArg,\n Resources extends ShopifyRestResources = ShopifyRestResources,\n> extends AdminContextInternal {}" + ] }, - "BillingContext": { - "filePath": "/server/authenticate/admin/billing/types.ts", - "name": "BillingContext", + "ShopifyClients": { + "filePath": "../../node_modules/@shopify/shopify-api/lib/clients/types.d.ts", + "name": "ShopifyClients", "description": "", "members": [ { - "filePath": "/server/authenticate/admin/billing/types.ts", + "filePath": "../../node_modules/@shopify/shopify-api/lib/clients/types.d.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.", - "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": "Using 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" - } - ] - } - ] + "name": "Rest", + "value": "typeof RestClient", + "description": "" }, { - "filePath": "/server/authenticate/admin/billing/types.ts", + "filePath": "../../node_modules/@shopify/shopify-api/lib/clients/types.d.ts", "syntaxKind": "PropertySignature", - "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" - } - ] - } - ] + "name": "Graphql", + "value": "typeof GraphqlClient", + "description": "" }, { - "filePath": "/server/authenticate/admin/billing/types.ts", + "filePath": "../../node_modules/@shopify/shopify-api/lib/clients/types.d.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" - } - ] - } - ] + "name": "Storefront", + "value": "typeof StorefrontClient", + "description": "" + }, + { + "filePath": "../../node_modules/@shopify/shopify-api/lib/clients/types.d.ts", + "syntaxKind": "PropertySignature", + "name": "graphqlProxy", + "value": "GraphqlProxy", + "description": "" } ], - "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 * Using 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 */\n require: (\n options: RequireBillingOptions,\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 ShopifyClients {\n Rest: typeof RestClient;\n Graphql: typeof GraphqlClient;\n Storefront: typeof StorefrontClient;\n graphqlProxy: GraphqlProxy;\n}" }, - "RequireBillingOptions": { - "filePath": "/server/authenticate/admin/billing/types.ts", - "name": "RequireBillingOptions", + "GraphqlClient": { + "filePath": "../../node_modules/@shopify/shopify-api/lib/clients/admin/graphql/client.d.ts", + "name": "GraphqlClient", "description": "", "members": [ { - "filePath": "/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": "../../node_modules/@shopify/shopify-api/lib/clients/admin/graphql/client.d.ts", + "syntaxKind": "PropertyDeclaration", + "name": "session", + "value": "Session", + "description": "" }, { - "filePath": "/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": "../../node_modules/@shopify/shopify-api/lib/clients/admin/graphql/client.d.ts", + "syntaxKind": "PropertyDeclaration", + "name": "client", + "value": "AdminApiClient", + "description": "" }, { - "filePath": "/server/authenticate/admin/billing/types.ts", - "syntaxKind": "PropertySignature", - "name": "isTest", - "value": "boolean", - "description": "", - "isOptional": true + "filePath": "../../node_modules/@shopify/shopify-api/lib/clients/admin/graphql/client.d.ts", + "syntaxKind": "PropertyDeclaration", + "name": "apiVersion", + "value": "ApiVersion", + "description": "" + }, + { + "filePath": "../../node_modules/@shopify/shopify-api/lib/clients/admin/graphql/client.d.ts", + "syntaxKind": "MethodDeclaration", + "name": "query", + "value": "(params: GraphqlParams) => Promise>", + "description": "" + }, + { + "filePath": "../../node_modules/@shopify/shopify-api/lib/clients/admin/graphql/client.d.ts", + "syntaxKind": "MethodDeclaration", + "name": "request", + "value": "(operation: Operation, options?: GraphqlQueryOptions) => Promise : T>>", + "description": "" } ], - "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 declare class GraphqlClient {\n static config: ConfigInterface;\n readonly session: Session;\n readonly client: AdminApiClient;\n readonly apiVersion?: ApiVersion;\n constructor(params: GraphqlClientParams);\n query(params: GraphqlParams): Promise>;\n request(operation: Operation, options?: GraphqlQueryOptions): Promise : T>>;\n private graphqlClass;\n}" }, - "RequestBillingOptions": { - "filePath": "/server/authenticate/admin/billing/types.ts", - "name": "RequestBillingOptions", + "GraphqlParams": { + "filePath": "../../node_modules/@shopify/shopify-api/lib/clients/types.d.ts", + "syntaxKind": "TypeAliasDeclaration", + "name": "GraphqlParams", + "value": "Omit", + "description": "", + "members": [] + }, + "RequestReturn": { + "filePath": "../../node_modules/@shopify/shopify-api/lib/clients/types.d.ts", + "name": "RequestReturn", "description": "", "members": [ { - "filePath": "/server/authenticate/admin/billing/types.ts", + "filePath": "../../node_modules/@shopify/shopify-api/lib/clients/types.d.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." - }, - { - "filePath": "/server/authenticate/admin/billing/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": "body", + "value": "T", + "description": "" }, { - "filePath": "/server/authenticate/admin/billing/types.ts", + "filePath": "../../node_modules/@shopify/shopify-api/lib/clients/types.d.ts", "syntaxKind": "PropertySignature", - "name": "returnUrl", - "value": "string", - "description": "The URL to return to after the merchant approves the payment.", - "isOptional": true + "name": "headers", + "value": "Headers", + "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}" + "value": "export interface RequestReturn {\n body: T;\n headers: Headers;\n}" }, - "CancelBillingOptions": { - "filePath": "/server/authenticate/admin/billing/types.ts", - "name": "CancelBillingOptions", + "GraphqlQueryOptions": { + "filePath": "../../node_modules/@shopify/shopify-api/lib/clients/types.d.ts", + "name": "GraphqlQueryOptions", "description": "", "members": [ { - "filePath": "/server/authenticate/admin/billing/types.ts", + "filePath": "../../node_modules/@shopify/shopify-api/lib/clients/types.d.ts", "syntaxKind": "PropertySignature", - "name": "subscriptionId", - "value": "string", - "description": "The ID of the subscription to cancel." + "name": "variables", + "value": "ApiClientRequestOptions[\"variables\"]", + "description": "", + "isOptional": true }, { - "filePath": "/server/authenticate/admin/billing/types.ts", + "filePath": "../../node_modules/@shopify/shopify-api/lib/clients/types.d.ts", "syntaxKind": "PropertySignature", - "name": "prorate", - "value": "boolean", - "description": "Whether to prorate the cancellation.\n\n\n\n\n", + "name": "headers", + "value": "Record", + "description": "", "isOptional": true }, { - "filePath": "/server/authenticate/admin/billing/types.ts", + "filePath": "../../node_modules/@shopify/shopify-api/lib/clients/types.d.ts", "syntaxKind": "PropertySignature", - "name": "isTest", - "value": "boolean", + "name": "retries", + "value": "number", "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}" + "value": "export interface GraphqlQueryOptions {\n variables?: ApiClientRequestOptions['variables'];\n headers?: Record;\n retries?: number;\n}" }, - "EmbeddedAdminContext": { - "filePath": "/server/authenticate/admin/types.ts", - "name": "EmbeddedAdminContext", + "StorefrontClient": { + "filePath": "../../node_modules/@shopify/shopify-api/lib/clients/storefront/client.d.ts", + "name": "StorefrontClient", "description": "", "members": [ { - "filePath": "/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", - "examples": [ - { - "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\";\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" - } - ] - } - ] - }, - { - "filePath": "/server/authenticate/admin/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`.", - "examples": [ - { - "title": "Redirecting to an app route", - "description": "Use the `redirect` helper to safely redirect between pages.", - "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" - } - ] - }, - { - "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 { 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": "/server/authenticate/admin/types.ts", - "syntaxKind": "PropertySignature", + "filePath": "../../node_modules/@shopify/shopify-api/lib/clients/storefront/client.d.ts", + "syntaxKind": "PropertyDeclaration", "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 { 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.", - "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" - } - ] - } - ] + "description": "" }, { - "filePath": "/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": "../../node_modules/@shopify/shopify-api/lib/clients/storefront/client.d.ts", + "syntaxKind": "PropertyDeclaration", + "name": "client", + "value": "StorefrontApiClient", + "description": "" }, { - "filePath": "/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": "../../node_modules/@shopify/shopify-api/lib/clients/storefront/client.d.ts", + "syntaxKind": "PropertyDeclaration", + "name": "apiVersion", + "value": "ApiVersion", + "description": "" }, { - "filePath": "/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" - } - ] - } - ] + "filePath": "../../node_modules/@shopify/shopify-api/lib/clients/storefront/client.d.ts", + "syntaxKind": "MethodDeclaration", + "name": "query", + "value": "(params: GraphqlParams) => Promise>", + "description": "" + }, + { + "filePath": "../../node_modules/@shopify/shopify-api/lib/clients/storefront/client.d.ts", + "syntaxKind": "MethodDeclaration", + "name": "request", + "value": "(operation: Operation, options?: GraphqlQueryOptions) => Promise : T>>", + "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 * // 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 declare class StorefrontClient {\n static config: ConfigInterface;\n readonly session: Session;\n readonly client: StorefrontApiClient;\n readonly apiVersion?: ApiVersion;\n constructor(params: GraphqlClientParams);\n query(params: GraphqlParams): Promise>;\n request(operation: Operation, options?: GraphqlQueryOptions): Promise : T>>;\n private storefrontClass;\n}" }, - "RedirectFunction": { - "filePath": "/server/authenticate/admin/helpers/redirect.ts", - "name": "RedirectFunction", + "GraphqlProxy": { + "filePath": "../../node_modules/@shopify/shopify-api/lib/clients/graphql_proxy/types.d.ts", + "name": "GraphqlProxy", "description": "", "params": [ { - "name": "url", - "description": "", - "value": "string", - "filePath": "/server/authenticate/admin/helpers/redirect.ts" - }, - { - "name": "init", + "name": "params", "description": "", - "value": "RedirectInit", - "isOptional": true, - "filePath": "/server/authenticate/admin/helpers/redirect.ts" + "value": "GraphqlProxyParams", + "filePath": "../../node_modules/@shopify/shopify-api/lib/clients/graphql_proxy/types.d.ts" } ], "returns": { - "filePath": "/server/authenticate/admin/helpers/redirect.ts", + "filePath": "../../node_modules/@shopify/shopify-api/lib/clients/graphql_proxy/types.d.ts", "description": "", - "name": "TypedResponse", - "value": "TypedResponse" + "name": "Promise", + "value": "Promise" }, - "value": "export type RedirectFunction = (\n url: string,\n init?: RedirectInit,\n) => TypedResponse;" + "value": "export type GraphqlProxy = (params: GraphqlProxyParams) => Promise;" }, - "RedirectInit": { - "filePath": "/server/authenticate/admin/helpers/redirect.ts", - "syntaxKind": "TypeAliasDeclaration", - "name": "RedirectInit", - "value": "number | (ResponseInit & {target?: RedirectTarget})", - "description": "" + "GraphqlProxyParams": { + "filePath": "../../node_modules/@shopify/shopify-api/lib/clients/graphql_proxy/types.d.ts", + "name": "GraphqlProxyParams", + "description": "", + "members": [ + { + "filePath": "../../node_modules/@shopify/shopify-api/lib/clients/graphql_proxy/types.d.ts", + "syntaxKind": "PropertySignature", + "name": "session", + "value": "Session", + "description": "" + }, + { + "filePath": "../../node_modules/@shopify/shopify-api/lib/clients/graphql_proxy/types.d.ts", + "syntaxKind": "PropertySignature", + "name": "rawBody", + "value": "string | Record", + "description": "" + } + ], + "value": "export interface GraphqlProxyParams {\n session: Session;\n rawBody: string | Record;\n}" }, - "RedirectTarget": { - "filePath": "/server/authenticate/admin/helpers/redirect.ts", + "ShopifyAuth": { + "filePath": "../../node_modules/@shopify/shopify-api/lib/auth/index.d.ts", "syntaxKind": "TypeAliasDeclaration", - "name": "RedirectTarget", - "value": "'_self' | '_parent' | '_top'", + "name": "ShopifyAuth", + "value": "{\n begin: OAuthBegin;\n callback: OAuthCallback;\n nonce: Nonce;\n safeCompare: SafeCompare;\n getEmbeddedAppUrl: GetEmbeddedAppUrl;\n buildEmbeddedAppUrl: BuildEmbeddedAppUrl;\n} & (FeatureEnabled extends true ? {\n tokenExchange: TokenExchange;\n} : Record)", "description": "" }, - "RestResourcesType": { - "filePath": "/server/types.ts", - "syntaxKind": "TypeAliasDeclaration", - "name": "RestResourcesType", - "value": "Config['restResources'] extends ShopifyRestResources\n ? Config['restResources']\n : ShopifyRestResources", - "description": "" + "OAuthBegin": { + "filePath": "../../node_modules/@shopify/shopify-api/lib/auth/oauth/oauth.d.ts", + "name": "OAuthBegin", + "description": "", + "params": [ + { + "name": "beginParams", + "description": "", + "value": "BeginParams", + "filePath": "../../node_modules/@shopify/shopify-api/lib/auth/oauth/oauth.d.ts" + } + ], + "returns": { + "filePath": "../../node_modules/@shopify/shopify-api/lib/auth/oauth/oauth.d.ts", + "description": "", + "name": "Promise", + "value": "Promise" + }, + "value": "export type OAuthBegin = (beginParams: BeginParams) => Promise;" }, - "AuthenticatePublic": { - "filePath": "/server/authenticate/public/types.ts", + "BeginParams": { + "filePath": "../../node_modules/@shopify/shopify-api/lib/auth/oauth/types.d.ts", + "name": "BeginParams", + "description": "", + "members": [ + { + "filePath": "../../node_modules/@shopify/shopify-api/lib/auth/oauth/types.d.ts", + "syntaxKind": "PropertySignature", + "name": "shop", + "value": "string", + "description": "" + }, + { + "filePath": "../../node_modules/@shopify/shopify-api/lib/auth/oauth/types.d.ts", + "syntaxKind": "PropertySignature", + "name": "callbackPath", + "value": "string", + "description": "" + }, + { + "filePath": "../../node_modules/@shopify/shopify-api/lib/auth/oauth/types.d.ts", + "syntaxKind": "PropertySignature", + "name": "isOnline", + "value": "boolean", + "description": "" + }, + { + "filePath": "../../node_modules/@shopify/shopify-api/lib/auth/oauth/types.d.ts", + "syntaxKind": "PropertySignature", + "name": "rawRequest", + "value": "AdapterRequest", + "description": "" + }, + { + "filePath": "../../node_modules/@shopify/shopify-api/lib/auth/oauth/types.d.ts", + "syntaxKind": "PropertySignature", + "name": "rawResponse", + "value": "AdapterResponse", + "description": "", + "isOptional": true + } + ], + "value": "export interface BeginParams extends AdapterArgs {\n shop: string;\n callbackPath: string;\n isOnline: boolean;\n}" + }, + "AdapterRequest": { + "filePath": "../../node_modules/@shopify/shopify-api/runtime/http/types.d.ts", "syntaxKind": "TypeAliasDeclaration", - "name": "AuthenticatePublic", - "value": "FeatureEnabled extends true\n ? AuthenticatePublicObject\n : AuthenticatePublicLegacy", + "name": "AdapterRequest", + "value": "any", "description": "" }, - "FeatureEnabled": { - "filePath": "/server/future/flags.ts", + "AdapterResponse": { + "filePath": "../../node_modules/@shopify/shopify-api/runtime/http/types.d.ts", "syntaxKind": "TypeAliasDeclaration", - "name": "FeatureEnabled", - "value": "Future extends FutureFlags\n ? Future[Flag] extends true\n ? true\n : false\n : false", + "name": "AdapterResponse", + "value": "any", "description": "" }, - "AuthenticatePublicObject": { - "filePath": "/server/authenticate/public/types.ts", - "name": "AuthenticatePublicObject", - "description": "", - "members": [ - { - "filePath": "/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" - } - ] - } - ] + "OAuthCallback": { + "filePath": "../../node_modules/@shopify/shopify-api/lib/auth/oauth/oauth.d.ts", + "name": "OAuthCallback", + "description": "", + "params": [ + { + "name": "callbackParams", + "description": "", + "value": "CallbackParams", + "filePath": "../../node_modules/@shopify/shopify-api/lib/auth/oauth/oauth.d.ts" + } + ], + "returns": { + "filePath": "../../node_modules/@shopify/shopify-api/lib/auth/oauth/oauth.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": "export type OAuthCallback = (callbackParams: CallbackParams) => Promise>;" + }, + "CallbackParams": { + "filePath": "../../node_modules/@shopify/shopify-api/lib/auth/oauth/types.d.ts", + "name": "CallbackParams", + "description": "", + "members": [ + { + "filePath": "../../node_modules/@shopify/shopify-api/lib/auth/oauth/types.d.ts", + "syntaxKind": "PropertySignature", + "name": "rawRequest", + "value": "AdapterRequest", + "description": "" }, { - "filePath": "/server/authenticate/public/types.ts", + "filePath": "../../node_modules/@shopify/shopify-api/lib/auth/oauth/types.d.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" - } - ] - } - ] + "name": "rawResponse", + "value": "AdapterResponse", + "description": "", + "isOptional": true } ], - "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}" + "value": "export interface CallbackParams extends AdapterArgs {\n}" }, - "AuthenticateCheckout": { - "filePath": "/server/authenticate/public/checkout/types.ts", - "name": "AuthenticateCheckout", + "Nonce": { + "filePath": "../../node_modules/@shopify/shopify-api/lib/auth/oauth/nonce.d.ts", + "name": "Nonce", + "description": "", + "params": [], + "returns": { + "filePath": "../../node_modules/@shopify/shopify-api/lib/auth/oauth/nonce.d.ts", + "description": "", + "name": "string", + "value": "string" + }, + "value": "export type Nonce = () => string;" + }, + "SafeCompare": { + "filePath": "../../node_modules/@shopify/shopify-api/lib/auth/oauth/safe-compare.d.ts", + "name": "SafeCompare", "description": "", "params": [ { - "name": "request", + "name": "strA", "description": "", - "value": "Request", - "filePath": "/server/authenticate/public/checkout/types.ts" + "value": "string | string[] | Record | number[]", + "filePath": "../../node_modules/@shopify/shopify-api/lib/auth/oauth/safe-compare.d.ts" }, { - "name": "options", + "name": "strB", "description": "", - "value": "AuthenticateCheckoutOptions", - "isOptional": true, - "filePath": "/server/authenticate/public/checkout/types.ts" + "value": "string | string[] | Record | number[]", + "filePath": "../../node_modules/@shopify/shopify-api/lib/auth/oauth/safe-compare.d.ts" } ], "returns": { - "filePath": "/server/authenticate/public/checkout/types.ts", + "filePath": "../../node_modules/@shopify/shopify-api/lib/auth/oauth/safe-compare.d.ts", "description": "", - "name": "Promise", - "value": "Promise" + "name": "boolean", + "value": "boolean" }, - "value": "export type AuthenticateCheckout = (\n request: Request,\n options?: AuthenticateCheckoutOptions,\n) => Promise;" + "value": "export type SafeCompare = (strA: string | Record | string[] | number[], strB: string | Record | string[] | number[]) => boolean;" }, - "AuthenticateCheckoutOptions": { - "filePath": "/server/authenticate/public/checkout/types.ts", - "name": "AuthenticateCheckoutOptions", + "GetEmbeddedAppUrl": { + "filePath": "../../node_modules/@shopify/shopify-api/lib/auth/get-embedded-app-url.d.ts", + "name": "GetEmbeddedAppUrl", "description": "", - "members": [ + "params": [ { - "filePath": "/server/authenticate/public/checkout/types.ts", - "syntaxKind": "PropertySignature", - "name": "corsHeaders", - "value": "string[]", + "name": "params", "description": "", - "isOptional": true + "value": "GetEmbeddedAppUrlParams", + "filePath": "../../node_modules/@shopify/shopify-api/lib/auth/get-embedded-app-url.d.ts" } ], - "value": "export interface AuthenticateCheckoutOptions {\n corsHeaders?: string[];\n}" + "returns": { + "filePath": "../../node_modules/@shopify/shopify-api/lib/auth/get-embedded-app-url.d.ts", + "description": "", + "name": "Promise", + "value": "Promise" + }, + "value": "export type GetEmbeddedAppUrl = (params: GetEmbeddedAppUrlParams) => Promise;" }, - "CheckoutContext": { - "filePath": "/server/authenticate/public/checkout/types.ts", - "name": "CheckoutContext", - "description": "Authenticated Context for a checkout request", + "GetEmbeddedAppUrlParams": { + "filePath": "../../node_modules/@shopify/shopify-api/lib/auth/types.d.ts", + "name": "GetEmbeddedAppUrlParams", + "description": "", "members": [ { - "filePath": "/server/authenticate/public/checkout/types.ts", + "filePath": "../../node_modules/@shopify/shopify-api/lib/auth/types.d.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" - } - ] - } - ] + "name": "rawRequest", + "value": "AdapterRequest", + "description": "" }, { - "filePath": "/server/authenticate/public/checkout/types.ts", + "filePath": "../../node_modules/@shopify/shopify-api/lib/auth/types.d.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" - } - ] - } - ] + "name": "rawResponse", + "value": "AdapterResponse", + "description": "", + "isOptional": true } ], - "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}" + "value": "export interface GetEmbeddedAppUrlParams extends AdapterArgs {\n}" }, - "AuthenticateAppProxy": { - "filePath": "/server/authenticate/public/appProxy/types.ts", - "name": "AuthenticateAppProxy", + "BuildEmbeddedAppUrl": { + "filePath": "../../node_modules/@shopify/shopify-api/lib/auth/get-embedded-app-url.d.ts", + "name": "BuildEmbeddedAppUrl", "description": "", "params": [ { - "name": "request", + "name": "host", "description": "", - "value": "Request", - "filePath": "/server/authenticate/public/appProxy/types.ts" + "value": "string", + "filePath": "../../node_modules/@shopify/shopify-api/lib/auth/get-embedded-app-url.d.ts" } ], "returns": { - "filePath": "/server/authenticate/public/appProxy/types.ts", + "filePath": "../../node_modules/@shopify/shopify-api/lib/auth/get-embedded-app-url.d.ts", "description": "", - "name": "Promise", - "value": "Promise" + "name": "string", + "value": "string" }, - "value": "export type AuthenticateAppProxy = (\n request: Request,\n) => Promise;" + "value": "export type BuildEmbeddedAppUrl = (host: string) => string;" }, - "AppProxyContext": { - "filePath": "/server/authenticate/public/appProxy/types.ts", - "name": "AppProxyContext", + "TokenExchange": { + "filePath": "../../node_modules/@shopify/shopify-api/lib/auth/oauth/token-exchange.d.ts", + "name": "TokenExchange", + "description": "", + "params": [ + { + "name": "params", + "description": "", + "value": "TokenExchangeParams", + "filePath": "../../node_modules/@shopify/shopify-api/lib/auth/oauth/token-exchange.d.ts" + } + ], + "returns": { + "filePath": "../../node_modules/@shopify/shopify-api/lib/auth/oauth/token-exchange.d.ts", + "description": "", + "name": "Promise<{\n session: Session;\n}>", + "value": "Promise<{\n session: Session;\n}>" + }, + "value": "export type TokenExchange = (params: TokenExchangeParams) => Promise<{\n session: Session;\n}>;" + }, + "TokenExchangeParams": { + "filePath": "../../node_modules/@shopify/shopify-api/lib/auth/oauth/token-exchange.d.ts", + "name": "TokenExchangeParams", "description": "", "members": [ { - "filePath": "/server/authenticate/public/appProxy/types.ts", + "filePath": "../../node_modules/@shopify/shopify-api/lib/auth/oauth/token-exchange.d.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." + "name": "shop", + "value": "string", + "description": "" }, { - "filePath": "/server/authenticate/public/appProxy/types.ts", + "filePath": "../../node_modules/@shopify/shopify-api/lib/auth/oauth/token-exchange.d.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." + "name": "sessionToken", + "value": "string", + "description": "" }, { - "filePath": "/server/authenticate/public/appProxy/types.ts", + "filePath": "../../node_modules/@shopify/shopify-api/lib/auth/oauth/token-exchange.d.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." + "name": "requestedTokenType", + "value": "RequestedTokenType", + "description": "" + } + ], + "value": "export interface TokenExchangeParams {\n shop: string;\n sessionToken: string;\n requestedTokenType: RequestedTokenType;\n}" + }, + "RequestedTokenType": { + "filePath": "../../node_modules/@shopify/shopify-api/lib/auth/oauth/token-exchange.d.ts", + "syntaxKind": "EnumDeclaration", + "name": "RequestedTokenType", + "value": "export declare enum RequestedTokenType {\n OnlineAccessToken = \"urn:shopify:params:oauth:token-type:online-access-token\",\n OfflineAccessToken = \"urn:shopify:params:oauth:token-type:offline-access-token\"\n}", + "members": [ + { + "filePath": "../../node_modules/@shopify/shopify-api/lib/auth/oauth/token-exchange.d.ts", + "name": "OnlineAccessToken", + "value": "urn:shopify:params:oauth:token-type:online-access-token" }, { - "filePath": "/server/authenticate/public/appProxy/types.ts", + "filePath": "../../node_modules/@shopify/shopify-api/lib/auth/oauth/token-exchange.d.ts", + "name": "OfflineAccessToken", + "value": "urn:shopify:params:oauth:token-type:offline-access-token" + } + ] + }, + "ShopifySession": { + "filePath": "../../node_modules/@shopify/shopify-api/lib/session/index.d.ts", + "syntaxKind": "TypeAliasDeclaration", + "name": "ShopifySession", + "value": "ReturnType", + "description": "", + "members": [ + { + "filePath": "../../node_modules/@shopify/shopify-api/lib/session/index.d.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.", - "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" - } - ] - } - ] + "name": "customAppSession", + "value": "(shop: string) => Session", + "description": "" + }, + { + "filePath": "../../node_modules/@shopify/shopify-api/lib/session/index.d.ts", + "syntaxKind": "PropertySignature", + "name": "getCurrentId", + "value": "({ isOnline, ...adapterArgs }: GetCurrentSessionIdParams) => Promise", + "description": "" + }, + { + "filePath": "../../node_modules/@shopify/shopify-api/lib/session/index.d.ts", + "syntaxKind": "PropertySignature", + "name": "getOfflineId", + "value": "(shop: string) => string", + "description": "" + }, + { + "filePath": "../../node_modules/@shopify/shopify-api/lib/session/index.d.ts", + "syntaxKind": "PropertySignature", + "name": "getJwtSessionId", + "value": "(shop: string, userId: string) => string", + "description": "" + }, + { + "filePath": "../../node_modules/@shopify/shopify-api/lib/session/index.d.ts", + "syntaxKind": "PropertySignature", + "name": "decodeSessionToken", + "value": "(token: string, { checkAudience }?: DecodeSessionTokenOptions) => Promise", + "description": "" } - ], - "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": "/server/authenticate/public/appProxy/types.ts", - "name": "LiquidResponseFunction", + "GetCurrentSessionIdParams": { + "filePath": "../../node_modules/@shopify/shopify-api/lib/session/types.d.ts", + "name": "GetCurrentSessionIdParams", "description": "", - "params": [ + "members": [ { - "name": "body", - "description": "", - "value": "string", - "filePath": "/server/authenticate/public/appProxy/types.ts" + "filePath": "../../node_modules/@shopify/shopify-api/lib/session/types.d.ts", + "syntaxKind": "PropertySignature", + "name": "isOnline", + "value": "boolean", + "description": "" }, { - "name": "initAndOptions", + "filePath": "../../node_modules/@shopify/shopify-api/lib/session/types.d.ts", + "syntaxKind": "PropertySignature", + "name": "rawRequest", + "value": "AdapterRequest", + "description": "" + }, + { + "filePath": "../../node_modules/@shopify/shopify-api/lib/session/types.d.ts", + "syntaxKind": "PropertySignature", + "name": "rawResponse", + "value": "AdapterResponse", "description": "", - "value": "number | (ResponseInit & Options)", - "isOptional": true, - "filePath": "/server/authenticate/public/appProxy/types.ts" + "isOptional": true } ], - "returns": { - "filePath": "/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;" + "value": "export interface GetCurrentSessionIdParams extends AdapterArgs {\n isOnline: boolean;\n}" }, - "Options": { - "filePath": "/server/authenticate/public/appProxy/types.ts", - "name": "Options", + "DecodeSessionTokenOptions": { + "filePath": "../../node_modules/@shopify/shopify-api/lib/session/decode-session-token.d.ts", + "name": "DecodeSessionTokenOptions", + "description": "", + "members": [ + { + "filePath": "../../node_modules/@shopify/shopify-api/lib/session/decode-session-token.d.ts", + "syntaxKind": "PropertySignature", + "name": "checkAudience", + "value": "boolean", + "description": "", + "isOptional": true + } + ], + "value": "export interface DecodeSessionTokenOptions {\n checkAudience?: boolean;\n}" + }, + "ShopifyUtils": { + "filePath": "../../node_modules/@shopify/shopify-api/lib/utils/index.d.ts", + "syntaxKind": "TypeAliasDeclaration", + "name": "ShopifyUtils", + "value": "ReturnType", + "description": "", + "members": [ + { + "filePath": "../../node_modules/@shopify/shopify-api/lib/utils/index.d.ts", + "syntaxKind": "PropertySignature", + "name": "sanitizeShop", + "value": "(shop: string, throwOnInvalid?: boolean) => string", + "description": "" + }, + { + "filePath": "../../node_modules/@shopify/shopify-api/lib/utils/index.d.ts", + "syntaxKind": "PropertySignature", + "name": "sanitizeHost", + "value": "(host: string, throwOnInvalid?: boolean) => string", + "description": "" + }, + { + "filePath": "../../node_modules/@shopify/shopify-api/lib/utils/index.d.ts", + "syntaxKind": "PropertySignature", + "name": "validateHmac", + "value": "(query: AuthQuery, { signator }?: { signator: HMACSignator; }) => Promise", + "description": "" + }, + { + "filePath": "../../node_modules/@shopify/shopify-api/lib/utils/index.d.ts", + "syntaxKind": "PropertySignature", + "name": "versionCompatible", + "value": "(referenceVersion: ApiVersion, currentVersion?: ApiVersion) => boolean", + "description": "" + }, + { + "filePath": "../../node_modules/@shopify/shopify-api/lib/utils/index.d.ts", + "syntaxKind": "PropertySignature", + "name": "versionPriorTo", + "value": "(referenceVersion: ApiVersion, currentVersion?: ApiVersion) => boolean", + "description": "" + }, + { + "filePath": "../../node_modules/@shopify/shopify-api/lib/utils/index.d.ts", + "syntaxKind": "PropertySignature", + "name": "shopAdminUrlToLegacyUrl", + "value": "(shopAdminUrl: string) => string", + "description": "" + }, + { + "filePath": "../../node_modules/@shopify/shopify-api/lib/utils/index.d.ts", + "syntaxKind": "PropertySignature", + "name": "legacyUrlToShopAdminUrl", + "value": "(legacyAdminUrl: string) => string", + "description": "" + } + ] + }, + "AuthQuery": { + "filePath": "../../node_modules/@shopify/shopify-api/lib/auth/oauth/types.d.ts", + "name": "AuthQuery", "description": "", "members": [ { - "filePath": "/server/authenticate/public/appProxy/types.ts", + "filePath": "../../node_modules/@shopify/shopify-api/lib/auth/oauth/types.d.ts", + "name": "[key: string]", + "value": "string | undefined" + }, + { + "filePath": "../../node_modules/@shopify/shopify-api/lib/auth/oauth/types.d.ts", + "syntaxKind": "PropertySignature", + "name": "hmac", + "value": "string", + "description": "", + "isOptional": true + }, + { + "filePath": "../../node_modules/@shopify/shopify-api/lib/auth/oauth/types.d.ts", "syntaxKind": "PropertySignature", - "name": "layout", - "value": "boolean", + "name": "signature", + "value": "string", "description": "", "isOptional": true } ], - "value": "interface Options {\n layout?: boolean;\n}" + "value": "export interface AuthQuery {\n [key: string]: string | undefined;\n hmac?: string;\n signature?: string;\n}" }, - "AppProxyContextWithSession": { - "filePath": "/server/authenticate/public/appProxy/types.ts", - "name": "AppProxyContextWithSession", + "HMACSignator": { + "filePath": "../../node_modules/@shopify/shopify-api/lib/utils/hmac-validator.d.ts", + "syntaxKind": "TypeAliasDeclaration", + "name": "HMACSignator", + "value": "'admin' | 'appProxy'", + "description": "" + }, + "ShopifyWebhooks": { + "filePath": "../../node_modules/@shopify/shopify-api/lib/webhooks/index.d.ts", + "syntaxKind": "TypeAliasDeclaration", + "name": "ShopifyWebhooks", + "value": "ReturnType", "description": "", "members": [ { - "filePath": "/server/authenticate/public/appProxy/types.ts", + "filePath": "../../node_modules/@shopify/shopify-api/lib/webhooks/index.d.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" - } - ] - } - ] + "name": "addHandlers", + "value": "(handlersToAdd: AddHandlersParams) => void", + "description": "" }, { - "filePath": "/server/authenticate/public/appProxy/types.ts", + "filePath": "../../node_modules/@shopify/shopify-api/lib/webhooks/index.d.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" - } - ] - } - ] + "name": "getTopicsAdded", + "value": "() => string[]", + "description": "" }, { - "filePath": "/server/authenticate/public/appProxy/types.ts", + "filePath": "../../node_modules/@shopify/shopify-api/lib/webhooks/index.d.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" - } - ] - } - ] + "name": "getHandlers", + "value": "(topic: string) => WebhookHandler[]", + "description": "" }, { - "filePath": "/server/authenticate/public/appProxy/types.ts", + "filePath": "../../node_modules/@shopify/shopify-api/lib/webhooks/index.d.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.", - "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" - } - ] - } - ] + "name": "register", + "value": "({ session, }: RegisterParams) => Promise", + "description": "" + }, + { + "filePath": "../../node_modules/@shopify/shopify-api/lib/webhooks/index.d.ts", + "syntaxKind": "PropertySignature", + "name": "process", + "value": "({ rawBody, ...adapterArgs }: WebhookProcessParams) => Promise", + "description": "" + }, + { + "filePath": "../../node_modules/@shopify/shopify-api/lib/webhooks/index.d.ts", + "syntaxKind": "PropertySignature", + "name": "validate", + "value": "({ rawBody, ...adapterArgs }: WebhookValidateParams) => Promise", + "description": "" } - ], - "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": "/server/clients/storefront/types.ts", - "name": "StorefrontContext", + "AddHandlersParams": { + "filePath": "../../node_modules/@shopify/shopify-api/lib/webhooks/types.d.ts", + "syntaxKind": "TypeAliasDeclaration", + "name": "AddHandlersParams", + "value": "Record", + "description": "", + "members": [] + }, + "RegisterParams": { + "filePath": "../../node_modules/@shopify/shopify-api/lib/webhooks/types.d.ts", + "name": "RegisterParams", "description": "", "members": [ { - "filePath": "/server/clients/storefront/types.ts", + "filePath": "../../node_modules/@shopify/shopify-api/lib/webhooks/types.d.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" - } - ] - } - ] + "name": "session", + "value": "Session", + "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 graphql: GraphQLClient;\n}" + "value": "export interface RegisterParams {\n session: Session;\n}" }, - "AuthenticatePublicLegacy": { - "filePath": "/server/authenticate/public/types.ts", + "Body": { + "filePath": "../../node_modules/@shopify/shopify-api/rest/types.d.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" + "name": "Body", + "value": "Record", + "description": "", + "members": [] }, - "AuthenticateWebhook": { - "filePath": "/server/authenticate/webhooks/types.ts", - "name": "AuthenticateWebhook", + "WebhookProcessParams": { + "filePath": "../../node_modules/@shopify/shopify-api/lib/webhooks/types.d.ts", + "name": "WebhookProcessParams", "description": "", - "params": [ + "members": [ { - "name": "request", + "filePath": "../../node_modules/@shopify/shopify-api/lib/webhooks/types.d.ts", + "syntaxKind": "PropertySignature", + "name": "rawBody", + "value": "string", + "description": "" + }, + { + "filePath": "../../node_modules/@shopify/shopify-api/lib/webhooks/types.d.ts", + "syntaxKind": "PropertySignature", + "name": "rawRequest", + "value": "AdapterRequest", + "description": "" + }, + { + "filePath": "../../node_modules/@shopify/shopify-api/lib/webhooks/types.d.ts", + "syntaxKind": "PropertySignature", + "name": "rawResponse", + "value": "AdapterResponse", "description": "", - "value": "Request", - "filePath": "/server/authenticate/webhooks/types.ts" + "isOptional": true } ], - "returns": { - "filePath": "/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>;" + "value": "export interface WebhookProcessParams extends AdapterArgs {\n rawBody: string;\n}" }, - "WebhookContext": { - "filePath": "/server/authenticate/webhooks/types.ts", + "WebhookValidateParams": { + "filePath": "../../node_modules/@shopify/shopify-api/lib/webhooks/types.d.ts", + "name": "WebhookValidateParams", + "description": "", + "members": [ + { + "filePath": "../../node_modules/@shopify/shopify-api/lib/webhooks/types.d.ts", + "syntaxKind": "PropertySignature", + "name": "rawBody", + "value": "string", + "description": "" + }, + { + "filePath": "../../node_modules/@shopify/shopify-api/lib/webhooks/types.d.ts", + "syntaxKind": "PropertySignature", + "name": "rawRequest", + "value": "AdapterRequest", + "description": "" + }, + { + "filePath": "../../node_modules/@shopify/shopify-api/lib/webhooks/types.d.ts", + "syntaxKind": "PropertySignature", + "name": "rawResponse", + "value": "AdapterResponse", + "description": "", + "isOptional": true + } + ], + "value": "export interface WebhookValidateParams extends WebhookProcessParams {\n}" + }, + "WebhookValidation": { + "filePath": "../../node_modules/@shopify/shopify-api/lib/webhooks/types.d.ts", "syntaxKind": "TypeAliasDeclaration", - "name": "WebhookContext", - "value": "WebhookContextWithoutSession | WebhookContextWithSession", + "name": "WebhookValidation", + "value": "WebhookValidationValid | WebhookValidationInvalid | WebhookValidationMissingHeaders", "description": "" }, - "WebhookContextWithoutSession": { - "filePath": "/server/authenticate/webhooks/types.ts", - "name": "WebhookContextWithoutSession", + "WebhookValidationValid": { + "filePath": "../../node_modules/@shopify/shopify-api/lib/webhooks/types.d.ts", + "name": "WebhookValidationValid", "description": "", "members": [ { - "filePath": "/server/authenticate/webhooks/types.ts", + "filePath": "../../node_modules/@shopify/shopify-api/lib/webhooks/types.d.ts", "syntaxKind": "PropertySignature", - "name": "session", - "value": "undefined", + "name": "valid", + "value": "true", "description": "" }, { - "filePath": "/server/authenticate/webhooks/types.ts", + "filePath": "../../node_modules/@shopify/shopify-api/lib/webhooks/types.d.ts", "syntaxKind": "PropertySignature", - "name": "admin", - "value": "undefined", + "name": "webhookId", + "value": "string", "description": "" }, { - "filePath": "/server/authenticate/webhooks/types.ts", + "filePath": "../../node_modules/@shopify/shopify-api/lib/webhooks/types.d.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" - } - ] - } - ] + "description": "" }, { - "filePath": "/server/authenticate/webhooks/types.ts", + "filePath": "../../node_modules/@shopify/shopify-api/lib/webhooks/types.d.ts", "syntaxKind": "PropertySignature", - "name": "shop", + "name": "domain", "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" - } - ] - } - ] + "description": "" }, { - "filePath": "/server/authenticate/webhooks/types.ts", + "filePath": "../../node_modules/@shopify/shopify-api/lib/webhooks/types.d.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" - } - ] - } - ] + "name": "hmac", + "value": "string", + "description": "" }, { - "filePath": "/server/authenticate/webhooks/types.ts", + "filePath": "../../node_modules/@shopify/shopify-api/lib/webhooks/types.d.ts", "syntaxKind": "PropertySignature", - "name": "webhookId", + "name": "topic", "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" - } - ] - } - ] + "description": "" + } + ], + "value": "export interface WebhookValidationValid extends WebhookFields {\n valid: true;\n}" + }, + "WebhookValidationInvalid": { + "filePath": "../../node_modules/@shopify/shopify-api/lib/webhooks/types.d.ts", + "name": "WebhookValidationInvalid", + "description": "", + "members": [ + { + "filePath": "../../node_modules/@shopify/shopify-api/lib/webhooks/types.d.ts", + "syntaxKind": "PropertySignature", + "name": "valid", + "value": "false", + "description": "" }, { - "filePath": "/server/authenticate/webhooks/types.ts", + "filePath": "../../node_modules/@shopify/shopify-api/lib/webhooks/types.d.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" - } - ] - } - ] + "name": "reason", + "value": "WebhookValidationErrorReason", + "description": "" } ], - "value": "export interface WebhookContextWithoutSession\n extends Context {\n session: undefined;\n admin: undefined;\n}" + "value": "export interface WebhookValidationInvalid {\n valid: false;\n reason: WebhookValidationErrorReason;\n}" }, - "JSONValue": { - "filePath": "/server/types.ts", - "syntaxKind": "TypeAliasDeclaration", - "name": "JSONValue", - "value": "string | number | boolean | null | JSONObject | JSONArray", - "description": "" + "WebhookValidationErrorReason": { + "filePath": "../../node_modules/@shopify/shopify-api/lib/webhooks/types.d.ts", + "syntaxKind": "EnumDeclaration", + "name": "WebhookValidationErrorReason", + "value": "export declare enum WebhookValidationErrorReason {\n MissingHeaders = \"missing_headers\",\n MissingBody = \"missing_body\",\n InvalidHmac = \"invalid_hmac\"\n}", + "members": [ + { + "filePath": "../../node_modules/@shopify/shopify-api/lib/webhooks/types.d.ts", + "name": "MissingHeaders", + "value": "missing_headers" + }, + { + "filePath": "../../node_modules/@shopify/shopify-api/lib/webhooks/types.d.ts", + "name": "MissingBody", + "value": "missing_body" + }, + { + "filePath": "../../node_modules/@shopify/shopify-api/lib/webhooks/types.d.ts", + "name": "InvalidHmac", + "value": "invalid_hmac" + } + ] }, - "JSONObject": { - "filePath": "/server/types.ts", - "name": "JSONObject", + "WebhookValidationMissingHeaders": { + "filePath": "../../node_modules/@shopify/shopify-api/lib/webhooks/types.d.ts", + "name": "WebhookValidationMissingHeaders", "description": "", "members": [ { - "filePath": "/server/types.ts", - "name": "[x: string]", - "value": "JSONValue" + "filePath": "../../node_modules/@shopify/shopify-api/lib/webhooks/types.d.ts", + "syntaxKind": "PropertySignature", + "name": "reason", + "value": "WebhookValidationErrorReason.MissingHeaders", + "description": "" + }, + { + "filePath": "../../node_modules/@shopify/shopify-api/lib/webhooks/types.d.ts", + "syntaxKind": "PropertySignature", + "name": "missingHeaders", + "value": "string[]", + "description": "" + }, + { + "filePath": "../../node_modules/@shopify/shopify-api/lib/webhooks/types.d.ts", + "syntaxKind": "PropertySignature", + "name": "valid", + "value": "false", + "description": "" } ], - "value": "interface JSONObject {\n [x: string]: JSONValue;\n}" + "value": "export interface WebhookValidationMissingHeaders extends WebhookValidationInvalid {\n reason: WebhookValidationErrorReason.MissingHeaders;\n missingHeaders: string[];\n}" }, - "JSONArray": { - "filePath": "/server/types.ts", - "name": "JSONArray", + "ShopifyBilling": { + "filePath": "../../node_modules/@shopify/shopify-api/lib/billing/index.d.ts", + "syntaxKind": "TypeAliasDeclaration", + "name": "ShopifyBilling", + "value": "ReturnType", "description": "", "members": [ { - "filePath": "/server/types.ts", + "filePath": "../../node_modules/@shopify/shopify-api/lib/billing/index.d.ts", "syntaxKind": "PropertySignature", - "name": "length", - "value": "number", - "description": "Gets or sets the length of the array. This is a number one higher than the highest index in the array." + "name": "check", + "value": "({ session, plans, isTest, returnObject, }: Params_1) => Promise>", + "description": "" }, { - "filePath": "/server/types.ts", - "syntaxKind": "MethodSignature", - "name": "toString", - "value": "() => string", - "description": "Returns a string representation of an array." + "filePath": "../../node_modules/@shopify/shopify-api/lib/billing/index.d.ts", + "syntaxKind": "PropertySignature", + "name": "request", + "value": "({ session, plan, isTest, returnUrl: returnUrlParam, returnObject, ...overrides }: Params_2) => Promise>", + "description": "" }, { - "filePath": "/server/types.ts", - "syntaxKind": "MethodSignature", - "name": "toLocaleString", - "value": "() => string", - "description": "Returns a string representation of an array. The elements are converted to string using their toLocaleString methods." + "filePath": "../../node_modules/@shopify/shopify-api/lib/billing/index.d.ts", + "syntaxKind": "PropertySignature", + "name": "cancel", + "value": "(subscriptionInfo: BillingCancelParams) => Promise", + "description": "" }, { - "filePath": "/server/types.ts", - "syntaxKind": "MethodSignature", - "name": "pop", - "value": "() => JSONValue", - "description": "Removes the last element from an array and returns it.\r\nIf the array is empty, undefined is returned and the array is not modified." - }, + "filePath": "../../node_modules/@shopify/shopify-api/lib/billing/index.d.ts", + "syntaxKind": "PropertySignature", + "name": "subscriptions", + "value": "({ session, }: BillingSubscriptionParams) => Promise", + "description": "" + } + ] + }, + "BillingCheckParams": { + "filePath": "../../node_modules/@shopify/shopify-api/lib/billing/types.d.ts", + "name": "BillingCheckParams", + "description": "", + "members": [ { - "filePath": "/server/types.ts", - "syntaxKind": "MethodSignature", - "name": "push", - "value": "(...items: JSONValue[]) => number", - "description": "Appends new elements to the end of an array, and returns the new length of the array." + "filePath": "../../node_modules/@shopify/shopify-api/lib/billing/types.d.ts", + "syntaxKind": "PropertySignature", + "name": "session", + "value": "Session", + "description": "" }, { - "filePath": "/server/types.ts", - "syntaxKind": "MethodSignature", - "name": "concat", - "value": "{ (...items: ConcatArray[]): JSONValue[]; (...items: (JSONValue | ConcatArray)[]): JSONValue[]; }", - "description": "Combines two or more arrays.\r\nThis method returns a new array without modifying any existing arrays." + "filePath": "../../node_modules/@shopify/shopify-api/lib/billing/types.d.ts", + "syntaxKind": "PropertySignature", + "name": "plans", + "value": "string | string[]", + "description": "" }, { - "filePath": "/server/types.ts", - "syntaxKind": "MethodSignature", - "name": "join", - "value": "(separator?: string) => string", - "description": "Adds all the elements of an array into a string, separated by the specified separator string." + "filePath": "../../node_modules/@shopify/shopify-api/lib/billing/types.d.ts", + "syntaxKind": "PropertySignature", + "name": "isTest", + "value": "boolean", + "description": "", + "isOptional": true }, { - "filePath": "/server/types.ts", - "syntaxKind": "MethodSignature", - "name": "reverse", - "value": "() => JSONValue[]", - "description": "Reverses the elements in an array in place.\r\nThis method mutates the array and returns a reference to the same array." - }, + "filePath": "../../node_modules/@shopify/shopify-api/lib/billing/types.d.ts", + "syntaxKind": "PropertySignature", + "name": "returnObject", + "value": "boolean", + "description": "", + "isOptional": true + } + ], + "value": "export interface BillingCheckParams {\n session: Session;\n plans: string[] | string;\n isTest?: boolean;\n returnObject?: boolean;\n}" + }, + "BillingCheckResponse": { + "filePath": "../../node_modules/@shopify/shopify-api/lib/billing/types.d.ts", + "syntaxKind": "TypeAliasDeclaration", + "name": "BillingCheckResponse", + "value": "Params['returnObject'] extends true ? BillingCheckResponseObject : boolean", + "description": "" + }, + "BillingRequestParams": { + "filePath": "../../node_modules/@shopify/shopify-api/lib/billing/types.d.ts", + "syntaxKind": "TypeAliasDeclaration", + "name": "BillingRequestParams", + "value": "{\n session: Session;\n plan: string;\n isTest?: boolean;\n returnUrl?: string;\n returnObject?: boolean;\n} & RequestConfigOverrides", + "description": "" + }, + "RequestConfigOverrides": { + "filePath": "../../node_modules/@shopify/shopify-api/lib/billing/types.d.ts", + "syntaxKind": "TypeAliasDeclaration", + "name": "RequestConfigOverrides", + "value": "Partial | Partial | Partial", + "description": "" + }, + "BillingConfigOneTimePlan": { + "filePath": "../../node_modules/@shopify/shopify-api/lib/billing/types.d.ts", + "name": "BillingConfigOneTimePlan", + "description": "", + "members": [ { - "filePath": "/server/types.ts", - "syntaxKind": "MethodSignature", - "name": "shift", - "value": "() => JSONValue", - "description": "Removes the first element from an array and returns it.\r\nIf the array is empty, undefined is returned and the array is not modified." + "filePath": "../../node_modules/@shopify/shopify-api/lib/billing/types.d.ts", + "syntaxKind": "PropertySignature", + "name": "interval", + "value": "BillingInterval.OneTime", + "description": "" }, { - "filePath": "/server/types.ts", - "syntaxKind": "MethodSignature", - "name": "slice", - "value": "(start?: number, end?: number) => JSONValue[]", - "description": "Returns a copy of a section of an array.\r\nFor both start and end, a negative index can be used to indicate an offset from the end of the array.\r\nFor example, -2 refers to the second to last element of the array." + "filePath": "../../node_modules/@shopify/shopify-api/lib/billing/types.d.ts", + "syntaxKind": "PropertySignature", + "name": "amount", + "value": "number", + "description": "" }, { - "filePath": "/server/types.ts", - "syntaxKind": "MethodSignature", - "name": "sort", - "value": "(compareFn?: (a: JSONValue, b: JSONValue) => number) => JSONArray", - "description": "Sorts an array in place.\r\nThis method mutates the array and returns a reference to the same array." - }, + "filePath": "../../node_modules/@shopify/shopify-api/lib/billing/types.d.ts", + "syntaxKind": "PropertySignature", + "name": "currencyCode", + "value": "string", + "description": "" + } + ], + "value": "export interface BillingConfigOneTimePlan extends BillingConfigPlan {\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": "/server/types.ts", - "syntaxKind": "MethodSignature", - "name": "splice", - "value": "{ (start: number, deleteCount?: number): JSONValue[]; (start: number, deleteCount: number, ...items: JSONValue[]): JSONValue[]; }", - "description": "Removes elements from an array and, if necessary, inserts new elements in their place, returning the deleted elements." + "filePath": "../../node_modules/@shopify/shopify-api/lib/types.d.ts", + "name": "OneTime", + "value": "ONE_TIME" }, { - "filePath": "/server/types.ts", - "syntaxKind": "MethodSignature", - "name": "unshift", - "value": "(...items: JSONValue[]) => number", - "description": "Inserts new elements at the start of an array, and returns the new length of the array." + "filePath": "../../node_modules/@shopify/shopify-api/lib/types.d.ts", + "name": "Every30Days", + "value": "EVERY_30_DAYS" }, { - "filePath": "/server/types.ts", - "syntaxKind": "MethodSignature", - "name": "indexOf", - "value": "(searchElement: JSONValue, fromIndex?: number) => number", - "description": "Returns the index of the first occurrence of a value in an array, or -1 if it is not present." + "filePath": "../../node_modules/@shopify/shopify-api/lib/types.d.ts", + "name": "Annual", + "value": "ANNUAL" }, { - "filePath": "/server/types.ts", - "syntaxKind": "MethodSignature", - "name": "lastIndexOf", - "value": "(searchElement: JSONValue, fromIndex?: number) => number", - "description": "Returns the index of the last occurrence of a specified value in an array, or -1 if it is not present." + "filePath": "../../node_modules/@shopify/shopify-api/lib/types.d.ts", + "name": "Usage", + "value": "USAGE" + } + ] + }, + "BillingConfigSubscriptionPlan": { + "filePath": "../../node_modules/@shopify/shopify-api/lib/billing/types.d.ts", + "name": "BillingConfigSubscriptionPlan", + "description": "", + "members": [ + { + "filePath": "../../node_modules/@shopify/shopify-api/lib/billing/types.d.ts", + "syntaxKind": "PropertySignature", + "name": "interval", + "value": "Exclude", + "description": "" }, { - "filePath": "/server/types.ts", - "syntaxKind": "MethodSignature", - "name": "every", - "value": "{ (predicate: (value: JSONValue, index: number, array: JSONValue[]) => value is S, thisArg?: any): this is S[]; (predicate: (value: JSONValue, index: number, array: JSONValue[]) => unknown, thisArg?: any): boolean; }", - "description": "Determines whether all the members of an array satisfy the specified test." + "filePath": "../../node_modules/@shopify/shopify-api/lib/billing/types.d.ts", + "syntaxKind": "PropertySignature", + "name": "trialDays", + "value": "number", + "description": "", + "isOptional": true }, { - "filePath": "/server/types.ts", - "syntaxKind": "MethodSignature", - "name": "some", - "value": "(predicate: (value: JSONValue, index: number, array: JSONValue[]) => unknown, thisArg?: any) => boolean", - "description": "Determines whether the specified callback function returns true for any element of an array." + "filePath": "../../node_modules/@shopify/shopify-api/lib/billing/types.d.ts", + "syntaxKind": "PropertySignature", + "name": "replacementBehavior", + "value": "BillingReplacementBehavior", + "description": "", + "isOptional": true }, { - "filePath": "/server/types.ts", - "syntaxKind": "MethodSignature", - "name": "forEach", - "value": "(callbackfn: (value: JSONValue, index: number, array: JSONValue[]) => void, thisArg?: any) => void", - "description": "Performs the specified action for each element in an array." + "filePath": "../../node_modules/@shopify/shopify-api/lib/billing/types.d.ts", + "syntaxKind": "PropertySignature", + "name": "discount", + "value": "BillingConfigSubscriptionPlanDiscount", + "description": "", + "isOptional": true }, { - "filePath": "/server/types.ts", - "syntaxKind": "MethodSignature", - "name": "map", - "value": "(callbackfn: (value: JSONValue, index: number, array: JSONValue[]) => U, thisArg?: any) => U[]", - "description": "Calls a defined callback function on each element of an array, and returns an array that contains the results." + "filePath": "../../node_modules/@shopify/shopify-api/lib/billing/types.d.ts", + "syntaxKind": "PropertySignature", + "name": "amount", + "value": "number", + "description": "" }, { - "filePath": "/server/types.ts", - "syntaxKind": "MethodSignature", - "name": "filter", - "value": "{ (predicate: (value: JSONValue, index: number, array: JSONValue[]) => value is S, thisArg?: any): S[]; (predicate: (value: JSONValue, index: number, array: JSONValue[]) => unknown, thisArg?: any): JSONValue[]; }", - "description": "Returns the elements of an array that meet the condition specified in a callback function." + "filePath": "../../node_modules/@shopify/shopify-api/lib/billing/types.d.ts", + "syntaxKind": "PropertySignature", + "name": "currencyCode", + "value": "string", + "description": "" + } + ], + "value": "export interface BillingConfigSubscriptionPlan extends BillingConfigPlan {\n interval: Exclude;\n trialDays?: number;\n replacementBehavior?: BillingReplacementBehavior;\n discount?: BillingConfigSubscriptionPlanDiscount;\n}" + }, + "RecurringBillingIntervals": { + "filePath": "../../node_modules/@shopify/shopify-api/lib/types.d.ts", + "syntaxKind": "TypeAliasDeclaration", + "name": "RecurringBillingIntervals", + "value": "RecurringBillingIntervals", + "description": "" + }, + "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": "/server/types.ts", - "syntaxKind": "MethodSignature", - "name": "reduce", - "value": "{ (callbackfn: (previousValue: JSONValue, currentValue: JSONValue, currentIndex: number, array: JSONValue[]) => JSONValue): JSONValue; (callbackfn: (previousValue: JSONValue, currentValue: JSONValue, currentIndex: number, array: JSONValue[]) => JSONValue, initialValue: JSONValue): JSONValue; (callbackfn: (previousValue: U, currentValue: JSONValue, currentIndex: number, array: JSONValue[]) => U, initialValue: U): U; }", - "description": "Calls the specified callback function for all the elements in an array. The return value of the callback function is the accumulated result, and is provided as an argument in the next call to the callback function." + "filePath": "../../node_modules/@shopify/shopify-api/lib/types.d.ts", + "name": "ApplyOnNextBillingCycle", + "value": "APPLY_ON_NEXT_BILLING_CYCLE" }, { - "filePath": "/server/types.ts", - "syntaxKind": "MethodSignature", - "name": "reduceRight", - "value": "{ (callbackfn: (previousValue: JSONValue, currentValue: JSONValue, currentIndex: number, array: JSONValue[]) => JSONValue): JSONValue; (callbackfn: (previousValue: JSONValue, currentValue: JSONValue, currentIndex: number, array: JSONValue[]) => JSONValue, initialValue: JSONValue): JSONValue; (callbackfn: (previousValue: U, currentValue: JSONValue, currentIndex: number, array: JSONValue[]) => U, initialValue: U): U; }", - "description": "Calls the specified callback function for all the elements in an array, in descending order. The return value of the callback function is the accumulated result, and is provided as an argument in the next call to the callback function." + "filePath": "../../node_modules/@shopify/shopify-api/lib/types.d.ts", + "name": "Standard", + "value": "STANDARD" + } + ] + }, + "BillingConfigSubscriptionPlanDiscount": { + "filePath": "../../node_modules/@shopify/shopify-api/lib/billing/types.d.ts", + "name": "BillingConfigSubscriptionPlanDiscount", + "description": "", + "members": [ + { + "filePath": "../../node_modules/@shopify/shopify-api/lib/billing/types.d.ts", + "syntaxKind": "PropertySignature", + "name": "durationLimitInIntervals", + "value": "number", + "description": "", + "isOptional": true }, { - "filePath": "/server/types.ts", - "syntaxKind": "MethodSignature", - "name": "find", - "value": "{ (predicate: (this: void, value: JSONValue, index: number, obj: JSONValue[]) => value is S, thisArg?: any): S; (predicate: (value: JSONValue, index: number, obj: JSONValue[]) => unknown, thisArg?: any): JSONValue; }", - "description": "Returns the value of the first element in the array where predicate is true, and undefined\r\notherwise." + "filePath": "../../node_modules/@shopify/shopify-api/lib/billing/types.d.ts", + "syntaxKind": "PropertySignature", + "name": "value", + "value": "BillingConfigSubscriptionPlanDiscountAmount | BillingConfigSubscriptionPlanDiscountPercentage", + "description": "" + } + ], + "value": "export interface BillingConfigSubscriptionPlanDiscount {\n durationLimitInIntervals?: number;\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/shopify-api/lib/billing/types.d.ts", + "syntaxKind": "PropertySignature", + "name": "amount", + "value": "number", + "description": "" }, { - "filePath": "/server/types.ts", - "syntaxKind": "MethodSignature", - "name": "findIndex", - "value": "(predicate: (value: JSONValue, index: number, obj: JSONValue[]) => unknown, thisArg?: any) => number", - "description": "Returns the index of the first element in the array where predicate is true, and -1\r\notherwise." + "filePath": "../../node_modules/@shopify/shopify-api/lib/billing/types.d.ts", + "syntaxKind": "PropertySignature", + "name": "percentage", + "value": "never", + "description": "", + "isOptional": true + } + ], + "value": "export interface BillingConfigSubscriptionPlanDiscountAmount {\n amount: number;\n percentage?: never;\n}" + }, + "BillingConfigSubscriptionPlanDiscountPercentage": { + "filePath": "../../node_modules/@shopify/shopify-api/lib/billing/types.d.ts", + "name": "BillingConfigSubscriptionPlanDiscountPercentage", + "description": "", + "members": [ + { + "filePath": "../../node_modules/@shopify/shopify-api/lib/billing/types.d.ts", + "syntaxKind": "PropertySignature", + "name": "amount", + "value": "never", + "description": "", + "isOptional": true }, { - "filePath": "/server/types.ts", - "syntaxKind": "MethodSignature", - "name": "fill", - "value": "(value: JSONValue, start?: number, end?: number) => JSONArray", - "description": "Changes all array elements from `start` to `end` index to a static `value` and returns the modified array" + "filePath": "../../node_modules/@shopify/shopify-api/lib/billing/types.d.ts", + "syntaxKind": "PropertySignature", + "name": "percentage", + "value": "number", + "description": "" + } + ], + "value": "export interface BillingConfigSubscriptionPlanDiscountPercentage {\n amount?: never;\n percentage: number;\n}" + }, + "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": "" }, { - "filePath": "/server/types.ts", - "syntaxKind": "MethodSignature", - "name": "copyWithin", - "value": "(target: number, start: number, end?: number) => JSONArray", - "description": "Returns the this object after copying a section of the array identified by start and end\r\nto the same array starting at position target" + "filePath": "../../node_modules/@shopify/shopify-api/lib/billing/types.d.ts", + "syntaxKind": "PropertySignature", + "name": "usageTerms", + "value": "string", + "description": "" }, { - "filePath": "/server/types.ts", - "syntaxKind": "MethodSignature", - "name": "entries", - "value": "() => IterableIterator<[number, JSONValue]>", - "description": "Returns an iterable of key, value pairs for every entry in the array" + "filePath": "../../node_modules/@shopify/shopify-api/lib/billing/types.d.ts", + "syntaxKind": "PropertySignature", + "name": "trialDays", + "value": "number", + "description": "", + "isOptional": true }, { - "filePath": "/server/types.ts", - "syntaxKind": "MethodSignature", - "name": "keys", - "value": "() => IterableIterator", - "description": "Returns an iterable of keys in the array" + "filePath": "../../node_modules/@shopify/shopify-api/lib/billing/types.d.ts", + "syntaxKind": "PropertySignature", + "name": "replacementBehavior", + "value": "BillingReplacementBehavior", + "description": "", + "isOptional": true }, { - "filePath": "/server/types.ts", - "syntaxKind": "MethodSignature", - "name": "values", - "value": "() => IterableIterator", - "description": "Returns an iterable of values in the array" + "filePath": "../../node_modules/@shopify/shopify-api/lib/billing/types.d.ts", + "syntaxKind": "PropertySignature", + "name": "amount", + "value": "number", + "description": "" }, { - "filePath": "/server/types.ts", - "syntaxKind": "MethodSignature", - "name": "includes", - "value": "(searchElement: JSONValue, fromIndex?: number) => boolean", - "description": "Determines whether an array includes a certain element, returning true or false as appropriate." + "filePath": "../../node_modules/@shopify/shopify-api/lib/billing/types.d.ts", + "syntaxKind": "PropertySignature", + "name": "currencyCode", + "value": "string", + "description": "" + } + ], + "value": "export interface BillingConfigUsagePlan extends BillingConfigPlan {\n interval: BillingInterval.Usage;\n usageTerms: string;\n trialDays?: number;\n replacementBehavior?: BillingReplacementBehavior;\n}" + }, + "BillingRequestResponse": { + "filePath": "../../node_modules/@shopify/shopify-api/lib/billing/types.d.ts", + "syntaxKind": "TypeAliasDeclaration", + "name": "BillingRequestResponse", + "value": "Params['returnObject'] extends true ? BillingRequestResponseObject : string", + "description": "" + }, + "BillingRequestResponseObject": { + "filePath": "../../node_modules/@shopify/shopify-api/lib/billing/types.d.ts", + "name": "BillingRequestResponseObject", + "description": "", + "members": [ + { + "filePath": "../../node_modules/@shopify/shopify-api/lib/billing/types.d.ts", + "syntaxKind": "PropertySignature", + "name": "confirmationUrl", + "value": "string", + "description": "" }, { - "filePath": "/server/types.ts", - "syntaxKind": "MethodSignature", - "name": "flatMap", - "value": "(callback: (this: This, value: JSONValue, index: number, array: JSONValue[]) => U | readonly U[], thisArg?: This) => U[]", - "description": "Calls a defined callback function on each element of an array. Then, flattens the result into\r\na new array.\r\nThis is identical to a map followed by flat with depth 1." + "filePath": "../../node_modules/@shopify/shopify-api/lib/billing/types.d.ts", + "syntaxKind": "PropertySignature", + "name": "oneTimePurchase", + "value": "OneTimePurchase", + "description": "", + "isOptional": true }, { - "filePath": "/server/types.ts", - "syntaxKind": "MethodSignature", - "name": "flat", - "value": "(this: A, depth?: D) => FlatArray[]", - "description": "Returns a new array with all sub-array elements concatenated into it recursively up to the\r\nspecified depth." + "filePath": "../../node_modules/@shopify/shopify-api/lib/billing/types.d.ts", + "syntaxKind": "PropertySignature", + "name": "appSubscription", + "value": "AppSubscription", + "description": "", + "isOptional": true + } + ], + "value": "export interface BillingRequestResponseObject {\n confirmationUrl: string;\n oneTimePurchase?: OneTimePurchase;\n appSubscription?: AppSubscription;\n}" + }, + "BillingCancelParams": { + "filePath": "../../node_modules/@shopify/shopify-api/lib/billing/types.d.ts", + "name": "BillingCancelParams", + "description": "", + "members": [ + { + "filePath": "../../node_modules/@shopify/shopify-api/lib/billing/types.d.ts", + "syntaxKind": "PropertySignature", + "name": "session", + "value": "Session", + "description": "" }, { - "filePath": "/server/types.ts", - "syntaxKind": "MethodSignature", - "name": "__@iterator@716", - "value": "() => IterableIterator", - "description": "Iterator" + "filePath": "../../node_modules/@shopify/shopify-api/lib/billing/types.d.ts", + "syntaxKind": "PropertySignature", + "name": "subscriptionId", + "value": "string", + "description": "" }, { - "filePath": "/server/types.ts", - "syntaxKind": "MethodSignature", - "name": "__@unscopables@718", - "value": "() => { copyWithin: boolean; entries: boolean; fill: boolean; find: boolean; findIndex: boolean; keys: boolean; values: boolean; }", - "description": "Returns an object whose properties have the value 'true'\r\nwhen they will be absent when used in a 'with' statement." + "filePath": "../../node_modules/@shopify/shopify-api/lib/billing/types.d.ts", + "syntaxKind": "PropertySignature", + "name": "prorate", + "value": "boolean", + "description": "", + "isOptional": true }, { - "filePath": "/server/types.ts", - "syntaxKind": "MethodSignature", - "name": "at", - "value": "(index: number) => JSONValue", - "description": "Takes an integer value and returns the item at that index, allowing for positive and negative integers. Negative integers count back from the last item in the array." + "filePath": "../../node_modules/@shopify/shopify-api/lib/billing/types.d.ts", + "syntaxKind": "PropertySignature", + "name": "isTest", + "value": "boolean", + "description": "", + "isOptional": true } ], - "value": "interface JSONArray extends Array {}" + "value": "export interface BillingCancelParams {\n session: Session;\n subscriptionId: string;\n prorate?: boolean;\n isTest?: boolean;\n}" }, - "WebhookContextWithSession": { - "filePath": "/server/authenticate/webhooks/types.ts", - "name": "WebhookContextWithSession", + "BillingSubscriptionParams": { + "filePath": "../../node_modules/@shopify/shopify-api/lib/billing/types.d.ts", + "name": "BillingSubscriptionParams", "description": "", "members": [ { - "filePath": "/server/authenticate/webhooks/types.ts", + "filePath": "../../node_modules/@shopify/shopify-api/lib/billing/types.d.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." - }, + "description": "" + } + ], + "value": "export interface BillingSubscriptionParams {\n session: Session;\n}" + }, + "ActiveSubscriptions": { + "filePath": "../../node_modules/@shopify/shopify-api/lib/billing/types.d.ts", + "name": "ActiveSubscriptions", + "description": "", + "members": [ { - "filePath": "/server/authenticate/webhooks/types.ts", + "filePath": "../../node_modules/@shopify/shopify-api/lib/billing/types.d.ts", "syntaxKind": "PropertySignature", - "name": "admin", - "value": "WebhookAdminContext", - "description": "An admin context for the webhook.\n\nReturned only if there is a session for the shop.", - "examples": [ - { - "title": "[V3] Webhook admin context", - "description": "With the `v3_webhookAdminContext` future flag enabled, 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.webhook(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/webhooks.tsx" - } - ] - }, - { - "title": "Webhook admin context", - "description": "Use the `admin` object in the context to interact with the Admin API. This format will be removed in V3 of the package.", - "tabs": [ - { - "code": "import { json, ActionFunctionArgs } from \"@remix-run/node\";\nimport { authenticate } from \"../shopify.server\";\n\nexport async function action({ request }: ActionFunctionArgs) {\n const { admin } = await authenticate.webhook(request);\n\n const response = await admin?.graphql.query({\n data: {\n query: `#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\n const productData = response?.body.data;\n return json({ data: productData.data });\n}", - "title": "/app/routes/webhooks.tsx" - } - ] - } - ] + "name": "activeSubscriptions", + "value": "AppSubscription[]", + "description": "" + } + ], + "value": "export interface ActiveSubscriptions {\n activeSubscriptions: AppSubscription[];\n}" + }, + "ShopifyLogger": { + "filePath": "../../node_modules/@shopify/shopify-api/lib/logger/index.d.ts", + "syntaxKind": "TypeAliasDeclaration", + "name": "ShopifyLogger", + "value": "ReturnType", + "description": "", + "members": [ + { + "filePath": "../../node_modules/@shopify/shopify-api/lib/logger/index.d.ts", + "syntaxKind": "PropertySignature", + "name": "log", + "value": "LoggerFunction", + "description": "" }, { - "filePath": "/server/authenticate/webhooks/types.ts", + "filePath": "../../node_modules/@shopify/shopify-api/lib/logger/index.d.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" - } - ] - } - ] + "name": "debug", + "value": "(message: string, context?: LogContext) => Promise", + "description": "" }, { - "filePath": "/server/authenticate/webhooks/types.ts", + "filePath": "../../node_modules/@shopify/shopify-api/lib/logger/index.d.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" - } - ] - } - ] + "name": "info", + "value": "(message: string, context?: LogContext) => Promise", + "description": "" }, { - "filePath": "/server/authenticate/webhooks/types.ts", + "filePath": "../../node_modules/@shopify/shopify-api/lib/logger/index.d.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" - } - ] - } - ] + "name": "warning", + "value": "(message: string, context?: LogContext) => Promise", + "description": "" }, { - "filePath": "/server/authenticate/webhooks/types.ts", + "filePath": "../../node_modules/@shopify/shopify-api/lib/logger/index.d.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" - } - ] - } - ] + "name": "error", + "value": "(message: string, context?: LogContext) => Promise", + "description": "" }, { - "filePath": "/server/authenticate/webhooks/types.ts", + "filePath": "../../node_modules/@shopify/shopify-api/lib/logger/index.d.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" - } - ] - } - ] + "name": "deprecated", + "value": "(version: string, message: string) => void", + "description": "" } - ], - "value": "export interface WebhookContextWithSession<\n Future extends FutureFlagOptions,\n Resources extends ShopifyRestResources,\n Topics = string | number | symbol,\n> extends Context {\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 session: Session;\n\n /**\n * An admin context for the webhook.\n *\n * Returned only if there is a session for the shop.\n *\n * @example\n * [V3] Webhook admin context.\n * With the `v3_webhookAdminContext` future flag enabled, use the `admin` object in the context to interact with the Admin API.\n * ```ts\n * // /app/routes/webhooks.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.webhook(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 * @example\n * Webhook admin context.\n * Use the `admin` object in the context to interact with the Admin API. This format will be removed in V3 of the package.\n * ```ts\n * // /app/routes/webhooks.tsx\n * import { json, ActionFunctionArgs } from \"@remix-run/node\";\n * import { authenticate } from \"../shopify.server\";\n *\n * export async function action({ request }: ActionFunctionArgs) {\n * const { admin } = await authenticate.webhook(request);\n *\n * const response = await admin?.graphql.query({\n * data: {\n * query: `#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 *\n * const productData = response?.body.data;\n * return json({ data: productData.data });\n * }\n * ```\n */\n admin: WebhookAdminContext;\n}" - }, - "WebhookAdminContext": { - "filePath": "/server/authenticate/webhooks/types.ts", - "syntaxKind": "TypeAliasDeclaration", - "name": "WebhookAdminContext", - "value": "FeatureEnabled extends true\n ? AdminApiContext\n : LegacyWebhookAdminApiContext", - "description": "" + ] }, - "LegacyWebhookAdminApiContext": { - "filePath": "/server/authenticate/webhooks/types.ts", - "name": "LegacyWebhookAdminApiContext", + "LoggerFunction": { + "filePath": "../../node_modules/@shopify/shopify-api/lib/logger/log.d.ts", + "name": "LoggerFunction", "description": "", - "members": [ + "params": [ { - "filePath": "/server/authenticate/webhooks/types.ts", - "syntaxKind": "PropertySignature", - "name": "rest", - "value": "RestClient & Resources", - "description": "A REST client." + "name": "severity", + "description": "", + "value": "LogSeverity", + "filePath": "../../node_modules/@shopify/shopify-api/lib/logger/log.d.ts" }, { - "filePath": "/server/authenticate/webhooks/types.ts", - "syntaxKind": "PropertySignature", - "name": "graphql", - "value": "InstanceType", - "description": "A GraphQL client." + "name": "message", + "description": "", + "value": "string", + "filePath": "../../node_modules/@shopify/shopify-api/lib/logger/log.d.ts" + }, + { + "name": "context", + "description": "", + "value": "Record", + "isOptional": true, + "filePath": "../../node_modules/@shopify/shopify-api/lib/logger/log.d.ts" } ], - "value": "export interface LegacyWebhookAdminApiContext<\n Resources extends ShopifyRestResources,\n> {\n /** A REST client. */\n rest: InstanceType & Resources;\n /** A GraphQL client. */\n graphql: InstanceType;\n}" + "returns": { + "filePath": "../../node_modules/@shopify/shopify-api/lib/logger/log.d.ts", + "description": "", + "name": "void", + "value": "void" + }, + "value": "export type LoggerFunction = (severity: LogSeverity, message: string, context?: Record) => void;" + }, + "LogContext": { + "filePath": "../../node_modules/@shopify/shopify-api/lib/logger/types.d.ts", + "syntaxKind": "TypeAliasDeclaration", + "name": "LogContext", + "value": "Record", + "description": "", + "members": [] }, "MandatoryTopics": { - "filePath": "/server/types.ts", + "filePath": "src/server/types.ts", "syntaxKind": "TypeAliasDeclaration", "name": "MandatoryTopics", "value": "'CUSTOMERS_DATA_REQUEST' | 'CUSTOMERS_REDACT' | 'SHOP_REDACT'", "description": "" }, "Unauthenticated": { - "filePath": "/server/unauthenticated/types.ts", + "filePath": "src/server/unauthenticated/types.ts", "name": "Unauthenticated", "description": "", "members": [ { - "filePath": "/server/unauthenticated/types.ts", + "filePath": "src/server/unauthenticated/types.ts", "syntaxKind": "PropertySignature", "name": "admin", "value": "GetUnauthenticatedAdminContext", @@ -5618,7 +14091,7 @@ ] }, { - "filePath": "/server/unauthenticated/types.ts", + "filePath": "src/server/unauthenticated/types.ts", "syntaxKind": "PropertySignature", "name": "storefront", "value": "GetUnauthenticatedStorefrontContext", @@ -5640,7 +14113,7 @@ "value": "export interface Unauthenticated {\n /**\n * Get an admin context by passing a shop\n *\n * **Warning** This should only be used for Requests that do not originate from Shopify.\n * You must do your own authentication before using this method.\n * This method throws an error if there is no session for the shop.\n *\n * @example\n * Responding to a request not controlled by 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 admin: GetUnauthenticatedAdminContext;\n\n /**\n * Get a storefront context by passing a shop\n *\n * **Warning** This should only be used for Requests that do not originate from Shopify.\n * You must do your own authentication before using this method.\n * This method throws an error if there is no session for the shop.\n *\n * @example\n * Responding to a request not controlled by Shopify\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 {storefront} = await shopify.unauthenticated.storefront(shop);\n * const response = await storefront.graphql(`{blogs(first: 10) { edges { node { id } } } }`);\n *\n * return json(await response.json());\n * }\n * ```\n */\n storefront: GetUnauthenticatedStorefrontContext;\n}" }, "GetUnauthenticatedAdminContext": { - "filePath": "/server/unauthenticated/admin/types.ts", + "filePath": "src/server/unauthenticated/admin/types.ts", "name": "GetUnauthenticatedAdminContext", "description": "", "params": [ @@ -5648,11 +14121,11 @@ "name": "shop", "description": "", "value": "string", - "filePath": "/server/unauthenticated/admin/types.ts" + "filePath": "src/server/unauthenticated/admin/types.ts" } ], "returns": { - "filePath": "/server/unauthenticated/admin/types.ts", + "filePath": "src/server/unauthenticated/admin/types.ts", "description": "", "name": "Promise>", "value": "Promise>" @@ -5660,12 +14133,12 @@ "value": "export type GetUnauthenticatedAdminContext<\n Resources extends ShopifyRestResources,\n> = (shop: string) => Promise>;" }, "UnauthenticatedAdminContext": { - "filePath": "/server/unauthenticated/admin/types.ts", + "filePath": "src/server/unauthenticated/admin/types.ts", "name": "UnauthenticatedAdminContext", "description": "", "members": [ { - "filePath": "/server/unauthenticated/admin/types.ts", + "filePath": "src/server/unauthenticated/admin/types.ts", "syntaxKind": "PropertySignature", "name": "session", "value": "Session", @@ -5684,7 +14157,7 @@ ] }, { - "filePath": "/server/unauthenticated/admin/types.ts", + "filePath": "src/server/unauthenticated/admin/types.ts", "syntaxKind": "PropertySignature", "name": "admin", "value": "AdminApiContext", @@ -5694,7 +14167,7 @@ "value": "export interface UnauthenticatedAdminContext<\n Resources extends ShopifyRestResources,\n> {\n /**\n * The session for the given shop.\n *\n * This comes from the session storage which `shopifyApp` uses to store sessions in your database of choice.\n *\n * This will always be an offline session. You can use to get shop-specific data.\n *\n * @example\n * Using the offline session.\n * Get your app's shop-specific data using the returned offline `session` object.\n * ```ts\n * // /app/routes/**\\/*.ts\n * import { LoaderFunctionArgs, json } from \"@remix-run/node\";\n * import { unauthenticated } from \"../shopify.server\";\n * import { getMyAppData } from \"~/db/model.server\";\n *\n * export const loader = async ({ request }: LoaderFunctionArgs) => {\n * const shop = getShopFromExternalRequest(request);\n * const { session } = await unauthenticated.admin(shop);\n * return json(await getMyAppData({shop: session.shop));\n * };\n * ```\n */\n session: Session;\n\n /**\n * Methods for interacting with the GraphQL / REST Admin APIs for the given store.\n */\n admin: AdminApiContext;\n}" }, "GetUnauthenticatedStorefrontContext": { - "filePath": "/server/unauthenticated/storefront/types.ts", + "filePath": "src/server/unauthenticated/storefront/types.ts", "name": "GetUnauthenticatedStorefrontContext", "description": "", "params": [ @@ -5702,11 +14175,11 @@ "name": "shop", "description": "", "value": "string", - "filePath": "/server/unauthenticated/storefront/types.ts" + "filePath": "src/server/unauthenticated/storefront/types.ts" } ], "returns": { - "filePath": "/server/unauthenticated/storefront/types.ts", + "filePath": "src/server/unauthenticated/storefront/types.ts", "description": "", "name": "Promise", "value": "Promise" @@ -5714,12 +14187,12 @@ "value": "export type GetUnauthenticatedStorefrontContext = (\n shop: string,\n) => Promise;" }, "UnauthenticatedStorefrontContext": { - "filePath": "/server/unauthenticated/storefront/types.ts", + "filePath": "src/server/unauthenticated/storefront/types.ts", "name": "UnauthenticatedStorefrontContext", "description": "", "members": [ { - "filePath": "/server/unauthenticated/storefront/types.ts", + "filePath": "src/server/unauthenticated/storefront/types.ts", "syntaxKind": "PropertySignature", "name": "session", "value": "Session", @@ -5738,7 +14211,7 @@ ] }, { - "filePath": "/server/unauthenticated/storefront/types.ts", + "filePath": "src/server/unauthenticated/storefront/types.ts", "syntaxKind": "PropertySignature", "name": "storefront", "value": "StorefrontContext", @@ -5748,19 +14221,19 @@ "value": "export interface UnauthenticatedStorefrontContext {\n /**\n * The session for the given shop.\n *\n * This comes from the session storage which `shopifyApp` uses to store sessions in your database of choice.\n *\n * This will always be an offline session. You can use this to get shop specific data.\n *\n * @example\n * Using the offline session.\n * Get your app's shop-specific data using the returned offline `session` object.\n * ```ts\n * // app/routes/**\\/.ts\n * import { LoaderFunctionArgs, json } from \"@remix-run/node\";\n * import { unauthenticated } from \"../shopify.server\";\n * import { getMyAppData } from \"~/db/model.server\";\n *\n * export const loader = async ({ request }: LoaderFunctionArgs) => {\n * const shop = getShopFromExternalRequest(request);\n * const { session } = await unauthenticated.storefront(shop);\n * return json(await getMyAppData({shop: session.shop));\n * };\n * ```\n */\n session: Session;\n\n /**\n * Method for interacting with the Shopify GraphQL Storefront API for the given store.\n */\n storefront: StorefrontContext;\n}" }, "SingleMerchantApp": { - "filePath": "/server/types.ts", + "filePath": "src/server/types.ts", "syntaxKind": "TypeAliasDeclaration", "name": "SingleMerchantApp", "value": "ShopifyAppBase & ShopifyAppLogin", "description": "" }, "ShopifyAppBase": { - "filePath": "/server/types.ts", + "filePath": "src/server/types.ts", "name": "ShopifyAppBase", "description": "", "members": [ { - "filePath": "/server/types.ts", + "filePath": "src/server/types.ts", "syntaxKind": "PropertySignature", "name": "sessionStorage", "value": "SessionStorageType", @@ -5779,7 +14252,7 @@ ] }, { - "filePath": "/server/types.ts", + "filePath": "src/server/types.ts", "syntaxKind": "PropertySignature", "name": "addDocumentResponseHeaders", "value": "AddDocumentResponseHeaders", @@ -5802,7 +14275,7 @@ ] }, { - "filePath": "/server/types.ts", + "filePath": "src/server/types.ts", "syntaxKind": "PropertySignature", "name": "registerWebhooks", "value": "RegisterWebhooks", @@ -5821,7 +14294,7 @@ ] }, { - "filePath": "/server/types.ts", + "filePath": "src/server/types.ts", "syntaxKind": "PropertySignature", "name": "authenticate", "value": "Authenticate", @@ -5844,7 +14317,7 @@ ] }, { - "filePath": "/server/types.ts", + "filePath": "src/server/types.ts", "syntaxKind": "PropertySignature", "name": "unauthenticated", "value": "Unauthenticated>", @@ -5870,12 +14343,12 @@ "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 * sesssionStorage: 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": "/server/types.ts", + "filePath": "src/server/types.ts", "name": "ShopifyAppLogin", "description": "", "members": [ { - "filePath": "/server/types.ts", + "filePath": "src/server/types.ts", "syntaxKind": "PropertySignature", "name": "login", "value": "Login", @@ -5901,7 +14374,7 @@ "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": "/server/types.ts", + "filePath": "src/server/types.ts", "name": "Login", "description": "", "params": [ @@ -5909,11 +14382,11 @@ "name": "request", "description": "", "value": "Request", - "filePath": "/server/types.ts" + "filePath": "src/server/types.ts" } ], "returns": { - "filePath": "/server/types.ts", + "filePath": "src/server/types.ts", "description": "", "name": "Promise", "value": "Promise" @@ -5921,12 +14394,12 @@ "value": "type Login = (request: Request) => Promise;" }, "LoginError": { - "filePath": "/server/types.ts", + "filePath": "src/server/types.ts", "name": "LoginError", "description": "", "members": [ { - "filePath": "/server/types.ts", + "filePath": "src/server/types.ts", "syntaxKind": "PropertySignature", "name": "shop", "value": "LoginErrorType", @@ -5937,25 +14410,25 @@ "value": "export interface LoginError {\n shop?: LoginErrorType;\n}" }, "LoginErrorType": { - "filePath": "/server/types.ts", + "filePath": "src/server/types.ts", "syntaxKind": "EnumDeclaration", "name": "LoginErrorType", "value": "export enum LoginErrorType {\n MissingShop = 'MISSING_SHOP',\n InvalidShop = 'INVALID_SHOP',\n}", "members": [ { - "filePath": "/server/types.ts", + "filePath": "src/server/types.ts", "name": "MissingShop", "value": "MISSING_SHOP" }, { - "filePath": "/server/types.ts", + "filePath": "src/server/types.ts", "name": "InvalidShop", "value": "INVALID_SHOP" } ] }, "AppStoreApp": { - "filePath": "/server/types.ts", + "filePath": "src/server/types.ts", "syntaxKind": "TypeAliasDeclaration", "name": "AppStoreApp", "value": "ShopifyAppBase & ShopifyAppLogin", @@ -5969,30 +14442,28 @@ "type": "FutureFlags", "typeDefinitions": { "FutureFlags": { - "filePath": "/server/future/flags.ts", + "filePath": "../../node_modules/@shopify/shopify-api/future/flags.d.ts", "name": "FutureFlags", "description": "", "members": [ { - "filePath": "/server/future/flags.ts", + "filePath": "../../node_modules/@shopify/shopify-api/future/flags.d.ts", "syntaxKind": "PropertySignature", - "name": "v3_webhookAdminContext", + "name": "unstable_tokenExchange", "value": "boolean", - "description": "When enabled, returns the same `admin` context (`AdminApiContext`) from `authenticate.webhook` that is returned from `authenticate.admin`.", - "isOptional": true, - "defaultValue": "false" + "description": "", + "isOptional": true }, { - "filePath": "/server/future/flags.ts", + "filePath": "../../node_modules/@shopify/shopify-api/future/flags.d.ts", "syntaxKind": "PropertySignature", - "name": "v3_authenticatePublic", + "name": "unstable_lineItemBilling", "value": "boolean", - "description": "When enabled authenticate.public() will not work. Use authenticate.public.checkout() instead.", - "isOptional": true, - "defaultValue": "false" + "description": "", + "isOptional": true } ], - "value": "export interface FutureFlags {\n /**\n * When enabled, returns the same `admin` context (`AdminApiContext`) from `authenticate.webhook` that is returned from `authenticate.admin`.\n *\n * @default false\n */\n v3_webhookAdminContext?: boolean;\n\n /**\n * When enabled authenticate.public() will not work. Use authenticate.public.checkout() instead.\n *\n * @default false\n */\n v3_authenticatePublic?: boolean;\n}" + "value": "export interface FutureFlags {\n unstable_tokenExchange?: boolean;\n unstable_lineItemBilling?: boolean;\n}" } } } @@ -6174,7 +14645,7 @@ "type": "GetUnauthenticatedAdminContext", "typeDefinitions": { "GetUnauthenticatedAdminContext": { - "filePath": "/server/unauthenticated/admin/types.ts", + "filePath": "src/server/unauthenticated/admin/types.ts", "name": "GetUnauthenticatedAdminContext", "description": "", "params": [ @@ -6182,11 +14653,11 @@ "name": "shop", "description": "", "value": "string", - "filePath": "/server/unauthenticated/admin/types.ts" + "filePath": "src/server/unauthenticated/admin/types.ts" } ], "returns": { - "filePath": "/server/unauthenticated/admin/types.ts", + "filePath": "src/server/unauthenticated/admin/types.ts", "description": "", "name": "Promise>", "value": "Promise>" @@ -6194,12 +14665,12 @@ "value": "export type GetUnauthenticatedAdminContext<\n Resources extends ShopifyRestResources,\n> = (shop: string) => Promise>;" }, "UnauthenticatedAdminContext": { - "filePath": "/server/unauthenticated/admin/types.ts", + "filePath": "src/server/unauthenticated/admin/types.ts", "name": "UnauthenticatedAdminContext", "description": "", "members": [ { - "filePath": "/server/unauthenticated/admin/types.ts", + "filePath": "src/server/unauthenticated/admin/types.ts", "syntaxKind": "PropertySignature", "name": "session", "value": "Session", @@ -6218,7 +14689,7 @@ ] }, { - "filePath": "/server/unauthenticated/admin/types.ts", + "filePath": "src/server/unauthenticated/admin/types.ts", "syntaxKind": "PropertySignature", "name": "admin", "value": "AdminApiContext", @@ -6227,13 +14698,252 @@ ], "value": "export interface UnauthenticatedAdminContext<\n Resources extends ShopifyRestResources,\n> {\n /**\n * The session for the given shop.\n *\n * This comes from the session storage which `shopifyApp` uses to store sessions in your database of choice.\n *\n * This will always be an offline session. You can use to get shop-specific data.\n *\n * @example\n * Using the offline session.\n * Get your app's shop-specific data using the returned offline `session` object.\n * ```ts\n * // /app/routes/**\\/*.ts\n * import { LoaderFunctionArgs, json } from \"@remix-run/node\";\n * import { unauthenticated } from \"../shopify.server\";\n * import { getMyAppData } from \"~/db/model.server\";\n *\n * export const loader = async ({ request }: LoaderFunctionArgs) => {\n * const shop = getShopFromExternalRequest(request);\n * const { session } = await unauthenticated.admin(shop);\n * return json(await getMyAppData({shop: session.shop));\n * };\n * ```\n */\n session: Session;\n\n /**\n * Methods for interacting with the GraphQL / REST Admin APIs for the given store.\n */\n admin: AdminApiContext;\n}" }, + "Session": { + "filePath": "../../node_modules/@shopify/shopify-api/lib/session/session.d.ts", + "name": "Session", + "description": "Stores App information from logged in merchants so they can make authenticated requests to the Admin API.", + "members": [ + { + "filePath": "../../node_modules/@shopify/shopify-api/lib/session/session.d.ts", + "syntaxKind": "PropertyDeclaration", + "name": "id", + "value": "string", + "description": "" + }, + { + "filePath": "../../node_modules/@shopify/shopify-api/lib/session/session.d.ts", + "syntaxKind": "PropertyDeclaration", + "name": "shop", + "value": "string", + "description": "" + }, + { + "filePath": "../../node_modules/@shopify/shopify-api/lib/session/session.d.ts", + "syntaxKind": "PropertyDeclaration", + "name": "state", + "value": "string", + "description": "" + }, + { + "filePath": "../../node_modules/@shopify/shopify-api/lib/session/session.d.ts", + "syntaxKind": "PropertyDeclaration", + "name": "isOnline", + "value": "boolean", + "description": "" + }, + { + "filePath": "../../node_modules/@shopify/shopify-api/lib/session/session.d.ts", + "syntaxKind": "PropertyDeclaration", + "name": "scope", + "value": "string", + "description": "" + }, + { + "filePath": "../../node_modules/@shopify/shopify-api/lib/session/session.d.ts", + "syntaxKind": "PropertyDeclaration", + "name": "expires", + "value": "Date", + "description": "" + }, + { + "filePath": "../../node_modules/@shopify/shopify-api/lib/session/session.d.ts", + "syntaxKind": "PropertyDeclaration", + "name": "accessToken", + "value": "string", + "description": "" + }, + { + "filePath": "../../node_modules/@shopify/shopify-api/lib/session/session.d.ts", + "syntaxKind": "PropertyDeclaration", + "name": "onlineAccessInfo", + "value": "OnlineAccessInfo", + "description": "" + }, + { + "filePath": "../../node_modules/@shopify/shopify-api/lib/session/session.d.ts", + "syntaxKind": "MethodDeclaration", + "name": "isActive", + "value": "(scopes: string | string[] | AuthScopes) => boolean", + "description": "" + }, + { + "filePath": "../../node_modules/@shopify/shopify-api/lib/session/session.d.ts", + "syntaxKind": "MethodDeclaration", + "name": "isScopeChanged", + "value": "(scopes: string | string[] | AuthScopes) => boolean", + "description": "" + }, + { + "filePath": "../../node_modules/@shopify/shopify-api/lib/session/session.d.ts", + "syntaxKind": "MethodDeclaration", + "name": "isExpired", + "value": "(withinMillisecondsOfExpiry?: number) => boolean", + "description": "" + }, + { + "filePath": "../../node_modules/@shopify/shopify-api/lib/session/session.d.ts", + "syntaxKind": "MethodDeclaration", + "name": "toObject", + "value": "() => SessionParams", + "description": "" + }, + { + "filePath": "../../node_modules/@shopify/shopify-api/lib/session/session.d.ts", + "syntaxKind": "MethodDeclaration", + "name": "equals", + "value": "(other: Session) => boolean", + "description": "" + }, + { + "filePath": "../../node_modules/@shopify/shopify-api/lib/session/session.d.ts", + "syntaxKind": "MethodDeclaration", + "name": "toPropertyArray", + "value": "() => [string, string | number | boolean][]", + "description": "" + } + ], + "value": "export declare class Session {\n static fromPropertyArray(entries: [string, string | number | boolean][]): Session;\n readonly id: string;\n shop: string;\n state: string;\n isOnline: boolean;\n scope?: string;\n expires?: Date;\n accessToken?: string;\n onlineAccessInfo?: OnlineAccessInfo;\n constructor(params: SessionParams);\n isActive(scopes: AuthScopes | string | string[]): boolean;\n isScopeChanged(scopes: AuthScopes | string | string[]): boolean;\n isExpired(withinMillisecondsOfExpiry?: number): boolean;\n toObject(): SessionParams;\n equals(other: Session | undefined): boolean;\n toPropertyArray(): [string, string | number | boolean][];\n}" + }, + "OnlineAccessInfo": { + "filePath": "../../node_modules/@shopify/shopify-api/lib/auth/oauth/types.d.ts", + "name": "OnlineAccessInfo", + "description": "", + "members": [ + { + "filePath": "../../node_modules/@shopify/shopify-api/lib/auth/oauth/types.d.ts", + "syntaxKind": "PropertySignature", + "name": "expires_in", + "value": "number", + "description": "" + }, + { + "filePath": "../../node_modules/@shopify/shopify-api/lib/auth/oauth/types.d.ts", + "syntaxKind": "PropertySignature", + "name": "associated_user_scope", + "value": "string", + "description": "" + }, + { + "filePath": "../../node_modules/@shopify/shopify-api/lib/auth/oauth/types.d.ts", + "syntaxKind": "PropertySignature", + "name": "associated_user", + "value": "{ id: number; first_name: string; last_name: string; email: string; email_verified: boolean; account_owner: boolean; locale: string; collaborator: boolean; }", + "description": "" + } + ], + "value": "export interface OnlineAccessInfo {\n expires_in: number;\n associated_user_scope: string;\n associated_user: {\n id: number;\n first_name: string;\n last_name: string;\n email: string;\n email_verified: boolean;\n account_owner: boolean;\n locale: string;\n collaborator: boolean;\n };\n}" + }, + "AuthScopes": { + "filePath": "../../node_modules/@shopify/shopify-api/lib/auth/scopes/index.d.ts", + "name": "AuthScopes", + "description": "", + "members": [ + { + "filePath": "../../node_modules/@shopify/shopify-api/lib/auth/scopes/index.d.ts", + "syntaxKind": "MethodDeclaration", + "name": "has", + "value": "(scope: string | string[] | AuthScopes) => boolean", + "description": "" + }, + { + "filePath": "../../node_modules/@shopify/shopify-api/lib/auth/scopes/index.d.ts", + "syntaxKind": "MethodDeclaration", + "name": "equals", + "value": "(otherScopes: string | string[] | AuthScopes) => boolean", + "description": "" + }, + { + "filePath": "../../node_modules/@shopify/shopify-api/lib/auth/scopes/index.d.ts", + "syntaxKind": "MethodDeclaration", + "name": "toString", + "value": "() => string", + "description": "" + }, + { + "filePath": "../../node_modules/@shopify/shopify-api/lib/auth/scopes/index.d.ts", + "syntaxKind": "MethodDeclaration", + "name": "toArray", + "value": "() => string[]", + "description": "" + } + ], + "value": "declare class AuthScopes {\n static SCOPE_DELIMITER: string;\n private compressedScopes;\n private expandedScopes;\n constructor(scopes: string | string[] | AuthScopes | undefined);\n has(scope: string | string[] | AuthScopes | undefined): boolean;\n equals(otherScopes: string | string[] | AuthScopes | undefined): boolean;\n toString(): string;\n toArray(): string[];\n private getImpliedScopes;\n}" + }, + "SessionParams": { + "filePath": "../../node_modules/@shopify/shopify-api/lib/session/types.d.ts", + "name": "SessionParams", + "description": "", + "members": [ + { + "filePath": "../../node_modules/@shopify/shopify-api/lib/session/types.d.ts", + "syntaxKind": "PropertySignature", + "name": "id", + "value": "string", + "description": "" + }, + { + "filePath": "../../node_modules/@shopify/shopify-api/lib/session/types.d.ts", + "syntaxKind": "PropertySignature", + "name": "shop", + "value": "string", + "description": "" + }, + { + "filePath": "../../node_modules/@shopify/shopify-api/lib/session/types.d.ts", + "syntaxKind": "PropertySignature", + "name": "state", + "value": "string", + "description": "" + }, + { + "filePath": "../../node_modules/@shopify/shopify-api/lib/session/types.d.ts", + "syntaxKind": "PropertySignature", + "name": "isOnline", + "value": "boolean", + "description": "" + }, + { + "filePath": "../../node_modules/@shopify/shopify-api/lib/session/types.d.ts", + "syntaxKind": "PropertySignature", + "name": "scope", + "value": "string", + "description": "", + "isOptional": true + }, + { + "filePath": "../../node_modules/@shopify/shopify-api/lib/session/types.d.ts", + "syntaxKind": "PropertySignature", + "name": "expires", + "value": "Date", + "description": "", + "isOptional": true + }, + { + "filePath": "../../node_modules/@shopify/shopify-api/lib/session/types.d.ts", + "syntaxKind": "PropertySignature", + "name": "accessToken", + "value": "string", + "description": "", + "isOptional": true + }, + { + "filePath": "../../node_modules/@shopify/shopify-api/lib/session/types.d.ts", + "syntaxKind": "PropertySignature", + "name": "onlineAccessInfo", + "value": "OnlineAccessInfo", + "description": "", + "isOptional": true + } + ], + "value": "export interface SessionParams {\n readonly id: string;\n shop: string;\n state: string;\n isOnline: boolean;\n scope?: string;\n expires?: Date;\n accessToken?: string;\n onlineAccessInfo?: OnlineAccessInfo;\n}" + }, "AdminApiContext": { - "filePath": "/server/clients/admin/types.ts", + "filePath": "src/server/clients/admin/types.ts", "name": "AdminApiContext", "description": "", "members": [ { - "filePath": "/server/clients/admin/types.ts", + "filePath": "src/server/clients/admin/types.ts", "syntaxKind": "PropertySignature", "name": "rest", "value": "RestClientWithResources", @@ -6284,33 +14994,279 @@ ] }, { - "filePath": "/server/clients/admin/types.ts", + "filePath": "src/server/clients/admin/types.ts", + "syntaxKind": "PropertySignature", + "name": "graphql", + "value": "GraphQLClient", + "description": "Methods for interacting with the Shopify Admin GraphQL API\n\n\n\n\n\n\n\n\n\n", + "examples": [ + { + "title": "Querying the GraphQL API", + "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" + } + ] + } + ] + } + ], + "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 graphql: GraphQLClient;\n}" + }, + "RestClientWithResources": { + "filePath": "src/server/clients/admin/rest.ts", + "syntaxKind": "TypeAliasDeclaration", + "name": "RestClientWithResources", + "value": "RemixRestClient & {resources: Resources}", + "description": "" + }, + "RemixRestClient": { + "filePath": "src/server/clients/admin/rest.ts", + "name": "RemixRestClient", + "description": "", + "members": [ + { + "filePath": "src/server/clients/admin/rest.ts", + "syntaxKind": "PropertyDeclaration", + "name": "session", + "value": "Session", + "description": "" + }, + { + "filePath": "src/server/clients/admin/rest.ts", + "syntaxKind": "MethodDeclaration", + "name": "get", + "value": "(params: GetRequestParams) => Promise", + "description": "Performs a GET request on the given path." + }, + { + "filePath": "src/server/clients/admin/rest.ts", + "syntaxKind": "MethodDeclaration", + "name": "post", + "value": "(params: PostRequestParams) => Promise", + "description": "Performs a POST request on the given path." + }, + { + "filePath": "src/server/clients/admin/rest.ts", + "syntaxKind": "MethodDeclaration", + "name": "put", + "value": "(params: PostRequestParams) => Promise", + "description": "Performs a PUT request on the given path." + }, + { + "filePath": "src/server/clients/admin/rest.ts", + "syntaxKind": "MethodDeclaration", + "name": "delete", + "value": "(params: GetRequestParams) => Promise", + "description": "Performs a DELETE request on the given path." + } + ], + "value": "class RemixRestClient {\n public session: Session;\n private params: AdminClientOptions['params'];\n private handleClientError: AdminClientOptions['handleClientError'];\n\n constructor({params, session, handleClientError}: AdminClientOptions) {\n this.params = params;\n this.handleClientError = handleClientError;\n this.session = session;\n }\n\n /**\n * Performs a GET request on the given path.\n */\n public async get(params: GetRequestParams) {\n return this.makeRequest({\n method: 'GET' as RequestParams['method'],\n ...params,\n });\n }\n\n /**\n * Performs a POST request on the given path.\n */\n public async post(params: PostRequestParams) {\n return this.makeRequest({\n method: 'POST' as RequestParams['method'],\n ...params,\n });\n }\n\n /**\n * Performs a PUT request on the given path.\n */\n public async put(params: PutRequestParams) {\n return this.makeRequest({\n method: 'PUT' as RequestParams['method'],\n ...params,\n });\n }\n\n /**\n * Performs a DELETE request on the given path.\n */\n public async delete(params: DeleteRequestParams) {\n return this.makeRequest({\n method: 'DELETE' as RequestParams['method'],\n ...params,\n });\n }\n\n protected async makeRequest(params: RequestParams): Promise {\n const originalClient = new this.params.api.clients.Rest({\n session: this.session,\n });\n const originalRequest = Reflect.get(originalClient, 'request');\n\n try {\n const apiResponse = await originalRequest.call(originalClient, params);\n\n // We use a separate client for REST requests and REST resources because we want to override the API library\n // client class to return a Response object instead.\n return new Response(JSON.stringify(apiResponse.body), {\n headers: apiResponse.headers,\n });\n } catch (error) {\n if (this.handleClientError) {\n throw await this.handleClientError({\n error,\n session: this.session,\n params: this.params,\n });\n } else throw new Error(error);\n }\n }\n}" + }, + "GetRequestParams": { + "filePath": "../../node_modules/@shopify/shopify-api/lib/clients/types.d.ts", + "name": "GetRequestParams", + "description": "", + "members": [ + { + "filePath": "../../node_modules/@shopify/shopify-api/lib/clients/types.d.ts", + "syntaxKind": "PropertySignature", + "name": "path", + "value": "string", + "description": "" + }, + { + "filePath": "../../node_modules/@shopify/shopify-api/lib/clients/types.d.ts", + "syntaxKind": "PropertySignature", + "name": "type", + "value": "DataType", + "description": "", + "isOptional": true + }, + { + "filePath": "../../node_modules/@shopify/shopify-api/lib/clients/types.d.ts", + "syntaxKind": "PropertySignature", + "name": "data", + "value": "string | Record", + "description": "", + "isOptional": true + }, + { + "filePath": "../../node_modules/@shopify/shopify-api/lib/clients/types.d.ts", + "syntaxKind": "PropertySignature", + "name": "query", + "value": "SearchParams", + "description": "", + "isOptional": true + }, + { + "filePath": "../../node_modules/@shopify/shopify-api/lib/clients/types.d.ts", + "syntaxKind": "PropertySignature", + "name": "extraHeaders", + "value": "HeaderParams", + "description": "", + "isOptional": true + }, + { + "filePath": "../../node_modules/@shopify/shopify-api/lib/clients/types.d.ts", + "syntaxKind": "PropertySignature", + "name": "tries", + "value": "number", + "description": "", + "isOptional": true + } + ], + "value": "export interface GetRequestParams {\n path: string;\n type?: DataType;\n data?: Record | string;\n query?: SearchParams;\n extraHeaders?: HeaderParams;\n tries?: number;\n}" + }, + "DataType": { + "filePath": "../../node_modules/@shopify/shopify-api/lib/clients/types.d.ts", + "syntaxKind": "EnumDeclaration", + "name": "DataType", + "value": "export declare enum DataType {\n JSON = \"application/json\",\n GraphQL = \"application/graphql\",\n URLEncoded = \"application/x-www-form-urlencoded\"\n}", + "members": [ + { + "filePath": "../../node_modules/@shopify/shopify-api/lib/clients/types.d.ts", + "name": "JSON", + "value": "application/json" + }, + { + "filePath": "../../node_modules/@shopify/shopify-api/lib/clients/types.d.ts", + "name": "GraphQL", + "value": "application/graphql" + }, + { + "filePath": "../../node_modules/@shopify/shopify-api/lib/clients/types.d.ts", + "name": "URLEncoded", + "value": "application/x-www-form-urlencoded" + } + ] + }, + "HeaderParams": { + "filePath": "../../node_modules/@shopify/shopify-api/lib/clients/types.d.ts", + "syntaxKind": "TypeAliasDeclaration", + "name": "HeaderParams", + "value": "Record", + "description": "", + "members": [] + }, + "PostRequestParams": { + "filePath": "../../node_modules/@shopify/shopify-api/lib/clients/types.d.ts", + "syntaxKind": "TypeAliasDeclaration", + "name": "PostRequestParams", + "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<\n ResponseWithType>>\n>;" + }, + "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": "", + "isOptional": true + }, + { + "filePath": "src/server/clients/types.ts", "syntaxKind": "PropertySignature", - "name": "graphql", - "value": "GraphQLClient", - "description": "Methods for interacting with the Shopify Admin GraphQL API\n\n\n\n\n\n\n\n\n\n", - "examples": [ - { - "title": "Querying the GraphQL API", - "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" - } - ] - } - ] + "name": "apiVersion", + "value": "ApiVersion", + "description": "", + "isOptional": true + }, + { + "filePath": "src/server/clients/types.ts", + "syntaxKind": "PropertySignature", + "name": "headers", + "value": "{ [key: string]: any; }", + "description": "", + "isOptional": true + }, + { + "filePath": "src/server/clients/types.ts", + "syntaxKind": "PropertySignature", + "name": "tries", + "value": "number", + "description": "", + "isOptional": true } ], - "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 graphql: GraphQLClient;\n}" + "value": "export interface GraphQLQueryOptions<\n Operation extends keyof Operations,\n Operations extends AllOperations,\n> {\n variables?: ApiClientRequestOptions['variables'];\n apiVersion?: ApiVersion;\n headers?: {[key: string]: any};\n tries?: number;\n}" }, - "RestClientWithResources": { - "filePath": "/server/clients/admin/rest.ts", - "syntaxKind": "TypeAliasDeclaration", - "name": "RestClientWithResources", - "value": "RemixRestClient & {resources: Resources}", - "description": "" + "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" + } + ] } } } @@ -6440,7 +15396,7 @@ "type": "GetUnauthenticatedStorefrontContext", "typeDefinitions": { "GetUnauthenticatedStorefrontContext": { - "filePath": "/server/unauthenticated/storefront/types.ts", + "filePath": "src/server/unauthenticated/storefront/types.ts", "name": "GetUnauthenticatedStorefrontContext", "description": "", "params": [ @@ -6448,11 +15404,11 @@ "name": "shop", "description": "", "value": "string", - "filePath": "/server/unauthenticated/storefront/types.ts" + "filePath": "src/server/unauthenticated/storefront/types.ts" } ], "returns": { - "filePath": "/server/unauthenticated/storefront/types.ts", + "filePath": "src/server/unauthenticated/storefront/types.ts", "description": "", "name": "Promise", "value": "Promise" @@ -6460,12 +15416,12 @@ "value": "export type GetUnauthenticatedStorefrontContext = (\n shop: string,\n) => Promise;" }, "UnauthenticatedStorefrontContext": { - "filePath": "/server/unauthenticated/storefront/types.ts", + "filePath": "src/server/unauthenticated/storefront/types.ts", "name": "UnauthenticatedStorefrontContext", "description": "", "members": [ { - "filePath": "/server/unauthenticated/storefront/types.ts", + "filePath": "src/server/unauthenticated/storefront/types.ts", "syntaxKind": "PropertySignature", "name": "session", "value": "Session", @@ -6484,7 +15440,7 @@ ] }, { - "filePath": "/server/unauthenticated/storefront/types.ts", + "filePath": "src/server/unauthenticated/storefront/types.ts", "syntaxKind": "PropertySignature", "name": "storefront", "value": "StorefrontContext", @@ -6493,13 +15449,252 @@ ], "value": "export interface UnauthenticatedStorefrontContext {\n /**\n * The session for the given shop.\n *\n * This comes from the session storage which `shopifyApp` uses to store sessions in your database of choice.\n *\n * This will always be an offline session. You can use this to get shop specific data.\n *\n * @example\n * Using the offline session.\n * Get your app's shop-specific data using the returned offline `session` object.\n * ```ts\n * // app/routes/**\\/.ts\n * import { LoaderFunctionArgs, json } from \"@remix-run/node\";\n * import { unauthenticated } from \"../shopify.server\";\n * import { getMyAppData } from \"~/db/model.server\";\n *\n * export const loader = async ({ request }: LoaderFunctionArgs) => {\n * const shop = getShopFromExternalRequest(request);\n * const { session } = await unauthenticated.storefront(shop);\n * return json(await getMyAppData({shop: session.shop));\n * };\n * ```\n */\n session: Session;\n\n /**\n * Method for interacting with the Shopify GraphQL Storefront API for the given store.\n */\n storefront: StorefrontContext;\n}" }, + "Session": { + "filePath": "../../node_modules/@shopify/shopify-api/lib/session/session.d.ts", + "name": "Session", + "description": "Stores App information from logged in merchants so they can make authenticated requests to the Admin API.", + "members": [ + { + "filePath": "../../node_modules/@shopify/shopify-api/lib/session/session.d.ts", + "syntaxKind": "PropertyDeclaration", + "name": "id", + "value": "string", + "description": "" + }, + { + "filePath": "../../node_modules/@shopify/shopify-api/lib/session/session.d.ts", + "syntaxKind": "PropertyDeclaration", + "name": "shop", + "value": "string", + "description": "" + }, + { + "filePath": "../../node_modules/@shopify/shopify-api/lib/session/session.d.ts", + "syntaxKind": "PropertyDeclaration", + "name": "state", + "value": "string", + "description": "" + }, + { + "filePath": "../../node_modules/@shopify/shopify-api/lib/session/session.d.ts", + "syntaxKind": "PropertyDeclaration", + "name": "isOnline", + "value": "boolean", + "description": "" + }, + { + "filePath": "../../node_modules/@shopify/shopify-api/lib/session/session.d.ts", + "syntaxKind": "PropertyDeclaration", + "name": "scope", + "value": "string", + "description": "" + }, + { + "filePath": "../../node_modules/@shopify/shopify-api/lib/session/session.d.ts", + "syntaxKind": "PropertyDeclaration", + "name": "expires", + "value": "Date", + "description": "" + }, + { + "filePath": "../../node_modules/@shopify/shopify-api/lib/session/session.d.ts", + "syntaxKind": "PropertyDeclaration", + "name": "accessToken", + "value": "string", + "description": "" + }, + { + "filePath": "../../node_modules/@shopify/shopify-api/lib/session/session.d.ts", + "syntaxKind": "PropertyDeclaration", + "name": "onlineAccessInfo", + "value": "OnlineAccessInfo", + "description": "" + }, + { + "filePath": "../../node_modules/@shopify/shopify-api/lib/session/session.d.ts", + "syntaxKind": "MethodDeclaration", + "name": "isActive", + "value": "(scopes: string | string[] | AuthScopes) => boolean", + "description": "" + }, + { + "filePath": "../../node_modules/@shopify/shopify-api/lib/session/session.d.ts", + "syntaxKind": "MethodDeclaration", + "name": "isScopeChanged", + "value": "(scopes: string | string[] | AuthScopes) => boolean", + "description": "" + }, + { + "filePath": "../../node_modules/@shopify/shopify-api/lib/session/session.d.ts", + "syntaxKind": "MethodDeclaration", + "name": "isExpired", + "value": "(withinMillisecondsOfExpiry?: number) => boolean", + "description": "" + }, + { + "filePath": "../../node_modules/@shopify/shopify-api/lib/session/session.d.ts", + "syntaxKind": "MethodDeclaration", + "name": "toObject", + "value": "() => SessionParams", + "description": "" + }, + { + "filePath": "../../node_modules/@shopify/shopify-api/lib/session/session.d.ts", + "syntaxKind": "MethodDeclaration", + "name": "equals", + "value": "(other: Session) => boolean", + "description": "" + }, + { + "filePath": "../../node_modules/@shopify/shopify-api/lib/session/session.d.ts", + "syntaxKind": "MethodDeclaration", + "name": "toPropertyArray", + "value": "() => [string, string | number | boolean][]", + "description": "" + } + ], + "value": "export declare class Session {\n static fromPropertyArray(entries: [string, string | number | boolean][]): Session;\n readonly id: string;\n shop: string;\n state: string;\n isOnline: boolean;\n scope?: string;\n expires?: Date;\n accessToken?: string;\n onlineAccessInfo?: OnlineAccessInfo;\n constructor(params: SessionParams);\n isActive(scopes: AuthScopes | string | string[]): boolean;\n isScopeChanged(scopes: AuthScopes | string | string[]): boolean;\n isExpired(withinMillisecondsOfExpiry?: number): boolean;\n toObject(): SessionParams;\n equals(other: Session | undefined): boolean;\n toPropertyArray(): [string, string | number | boolean][];\n}" + }, + "OnlineAccessInfo": { + "filePath": "../../node_modules/@shopify/shopify-api/lib/auth/oauth/types.d.ts", + "name": "OnlineAccessInfo", + "description": "", + "members": [ + { + "filePath": "../../node_modules/@shopify/shopify-api/lib/auth/oauth/types.d.ts", + "syntaxKind": "PropertySignature", + "name": "expires_in", + "value": "number", + "description": "" + }, + { + "filePath": "../../node_modules/@shopify/shopify-api/lib/auth/oauth/types.d.ts", + "syntaxKind": "PropertySignature", + "name": "associated_user_scope", + "value": "string", + "description": "" + }, + { + "filePath": "../../node_modules/@shopify/shopify-api/lib/auth/oauth/types.d.ts", + "syntaxKind": "PropertySignature", + "name": "associated_user", + "value": "{ id: number; first_name: string; last_name: string; email: string; email_verified: boolean; account_owner: boolean; locale: string; collaborator: boolean; }", + "description": "" + } + ], + "value": "export interface OnlineAccessInfo {\n expires_in: number;\n associated_user_scope: string;\n associated_user: {\n id: number;\n first_name: string;\n last_name: string;\n email: string;\n email_verified: boolean;\n account_owner: boolean;\n locale: string;\n collaborator: boolean;\n };\n}" + }, + "AuthScopes": { + "filePath": "../../node_modules/@shopify/shopify-api/lib/auth/scopes/index.d.ts", + "name": "AuthScopes", + "description": "", + "members": [ + { + "filePath": "../../node_modules/@shopify/shopify-api/lib/auth/scopes/index.d.ts", + "syntaxKind": "MethodDeclaration", + "name": "has", + "value": "(scope: string | string[] | AuthScopes) => boolean", + "description": "" + }, + { + "filePath": "../../node_modules/@shopify/shopify-api/lib/auth/scopes/index.d.ts", + "syntaxKind": "MethodDeclaration", + "name": "equals", + "value": "(otherScopes: string | string[] | AuthScopes) => boolean", + "description": "" + }, + { + "filePath": "../../node_modules/@shopify/shopify-api/lib/auth/scopes/index.d.ts", + "syntaxKind": "MethodDeclaration", + "name": "toString", + "value": "() => string", + "description": "" + }, + { + "filePath": "../../node_modules/@shopify/shopify-api/lib/auth/scopes/index.d.ts", + "syntaxKind": "MethodDeclaration", + "name": "toArray", + "value": "() => string[]", + "description": "" + } + ], + "value": "declare class AuthScopes {\n static SCOPE_DELIMITER: string;\n private compressedScopes;\n private expandedScopes;\n constructor(scopes: string | string[] | AuthScopes | undefined);\n has(scope: string | string[] | AuthScopes | undefined): boolean;\n equals(otherScopes: string | string[] | AuthScopes | undefined): boolean;\n toString(): string;\n toArray(): string[];\n private getImpliedScopes;\n}" + }, + "SessionParams": { + "filePath": "../../node_modules/@shopify/shopify-api/lib/session/types.d.ts", + "name": "SessionParams", + "description": "", + "members": [ + { + "filePath": "../../node_modules/@shopify/shopify-api/lib/session/types.d.ts", + "syntaxKind": "PropertySignature", + "name": "id", + "value": "string", + "description": "" + }, + { + "filePath": "../../node_modules/@shopify/shopify-api/lib/session/types.d.ts", + "syntaxKind": "PropertySignature", + "name": "shop", + "value": "string", + "description": "" + }, + { + "filePath": "../../node_modules/@shopify/shopify-api/lib/session/types.d.ts", + "syntaxKind": "PropertySignature", + "name": "state", + "value": "string", + "description": "" + }, + { + "filePath": "../../node_modules/@shopify/shopify-api/lib/session/types.d.ts", + "syntaxKind": "PropertySignature", + "name": "isOnline", + "value": "boolean", + "description": "" + }, + { + "filePath": "../../node_modules/@shopify/shopify-api/lib/session/types.d.ts", + "syntaxKind": "PropertySignature", + "name": "scope", + "value": "string", + "description": "", + "isOptional": true + }, + { + "filePath": "../../node_modules/@shopify/shopify-api/lib/session/types.d.ts", + "syntaxKind": "PropertySignature", + "name": "expires", + "value": "Date", + "description": "", + "isOptional": true + }, + { + "filePath": "../../node_modules/@shopify/shopify-api/lib/session/types.d.ts", + "syntaxKind": "PropertySignature", + "name": "accessToken", + "value": "string", + "description": "", + "isOptional": true + }, + { + "filePath": "../../node_modules/@shopify/shopify-api/lib/session/types.d.ts", + "syntaxKind": "PropertySignature", + "name": "onlineAccessInfo", + "value": "OnlineAccessInfo", + "description": "", + "isOptional": true + } + ], + "value": "export interface SessionParams {\n readonly id: string;\n shop: string;\n state: string;\n isOnline: boolean;\n scope?: string;\n expires?: Date;\n accessToken?: string;\n onlineAccessInfo?: OnlineAccessInfo;\n}" + }, "StorefrontContext": { - "filePath": "/server/clients/storefront/types.ts", + "filePath": "src/server/clients/storefront/types.ts", "name": "StorefrontContext", "description": "", "members": [ { - "filePath": "/server/clients/storefront/types.ts", + "filePath": "src/server/clients/storefront/types.ts", "syntaxKind": "PropertySignature", "name": "graphql", "value": "GraphQLClient", @@ -6519,6 +15714,116 @@ } ], "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}" + }, + "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<\n ResponseWithType>>\n>;" + }, + "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": "", + "isOptional": true + }, + { + "filePath": "src/server/clients/types.ts", + "syntaxKind": "PropertySignature", + "name": "apiVersion", + "value": "ApiVersion", + "description": "", + "isOptional": true + }, + { + "filePath": "src/server/clients/types.ts", + "syntaxKind": "PropertySignature", + "name": "headers", + "value": "{ [key: string]: any; }", + "description": "", + "isOptional": true + }, + { + "filePath": "src/server/clients/types.ts", + "syntaxKind": "PropertySignature", + "name": "tries", + "value": "number", + "description": "", + "isOptional": true + } + ], + "value": "export interface GraphQLQueryOptions<\n Operation extends keyof Operations,\n Operations extends AllOperations,\n> {\n variables?: ApiClientRequestOptions['variables'];\n apiVersion?: ApiVersion;\n headers?: {[key: string]: any};\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" + } + ] } } } diff --git a/packages/shopify-app-remix/package.json b/packages/shopify-app-remix/package.json index d59a0102fc..2873790713 100644 --- a/packages/shopify-app-remix/package.json +++ b/packages/shopify-app-remix/package.json @@ -51,30 +51,26 @@ ], "devDependencies": { "@remix-run/react": "^2.5.0", - "@shopify/generate-docs": "^0.11.1", + "@shopify/generate-docs": "^0.13.1", "@shopify/polaris": "^11.8.0", "@shopify/react-testing": "^5.1.3", "@shopify/shopify-app-session-storage-memory": "^2.0.3", - "@types/jest": "^29.5.1", "@types/jsonwebtoken": "^9.0.5", "@types/react": "^18.2.18", "@types/semver": "^7.5.6", - "jest": "^29.5.0", "jest-fetch-mock": "^3.0.3", "jsonwebtoken": "^9.0.2", "react": "^18.2.0", - "react-dom": "^18.2.0", - "ts-jest": "^29.1.0" + "react-dom": "^18.2.0" }, "dependencies": { "@remix-run/server-runtime": "^2.0.0", "@shopify/admin-api-client": "^0.2.1", - "@shopify/shopify-api": "^9.0.1", + "@shopify/shopify-api": "^9.0.2", "@shopify/shopify-app-session-storage": "^2.0.3", "@shopify/storefront-api-client": "^0.2.1", "isbot": "^3.7.1", - "semver": "^7.5.4", - "tslib": "^2.6.2" + "semver": "^7.5.4" }, "peerDependencies": { "@remix-run/react": "*", diff --git a/packages/shopify-app-remix/src/server/__test-helpers/const.ts b/packages/shopify-app-remix/src/server/__test-helpers/const.ts index d41729a69c..63442bc23f 100644 --- a/packages/shopify-app-remix/src/server/__test-helpers/const.ts +++ b/packages/shopify-app-remix/src/server/__test-helpers/const.ts @@ -5,6 +5,7 @@ export const API_KEY = 'testApiKey'; export const APP_URL = 'https://my-test-app.myshopify.io'; export const SHOPIFY_HOST = 'totally-real-host.myshopify.io'; export const BASE64_HOST = Buffer.from(SHOPIFY_HOST).toString('base64'); -export const TEST_SHOP = 'test-shop.myshopify.com'; +export const TEST_SHOP_NAME = 'test-shop'; +export const TEST_SHOP = `${TEST_SHOP_NAME}.myshopify.com`; export const GRAPHQL_URL = `https://${TEST_SHOP}/admin/api/${LATEST_API_VERSION}/graphql.json`; export const USER_ID = 12345; diff --git a/packages/shopify-app-remix/src/server/__test-helpers/setup-valid-session.ts b/packages/shopify-app-remix/src/server/__test-helpers/setup-valid-session.ts index f79601bbbb..ac4cfa4035 100644 --- a/packages/shopify-app-remix/src/server/__test-helpers/setup-valid-session.ts +++ b/packages/shopify-app-remix/src/server/__test-helpers/setup-valid-session.ts @@ -5,14 +5,15 @@ import {TEST_SHOP, USER_ID} from './const'; export async function setUpValidSession( sessionStorage: SessionStorage, - isOnline = false, + sessionParams?: Partial, ): Promise { const overrides: Partial = {}; let id = `offline_${TEST_SHOP}`; - if (isOnline) { + if (sessionParams?.isOnline) { id = `${TEST_SHOP}_${USER_ID}`; // Expires one day from now - overrides.expires = new Date(Date.now() + 1000 * 3600 * 24); + overrides.expires = + sessionParams.expires || new Date(Date.now() + 1000 * 3600 * 24); overrides.onlineAccessInfo = { associated_user_scope: 'testScope', expires_in: 3600 * 24, @@ -32,7 +33,7 @@ export async function setUpValidSession( const session = new Session({ id, shop: TEST_SHOP, - isOnline, + isOnline: Boolean(sessionParams?.isOnline), state: 'test', accessToken: 'totally_real_token', scope: 'testScope', diff --git a/packages/shopify-app-remix/src/server/__test-helpers/test-config.ts b/packages/shopify-app-remix/src/server/__test-helpers/test-config.ts index d323b072b7..9f9104f793 100644 --- a/packages/shopify-app-remix/src/server/__test-helpers/test-config.ts +++ b/packages/shopify-app-remix/src/server/__test-helpers/test-config.ts @@ -21,6 +21,7 @@ import {API_KEY, API_SECRET_KEY, APP_URL} from './const'; const TEST_FUTURE_FLAGS: Required<{[key in keyof FutureFlags]: true}> = { v3_authenticatePublic: true, v3_webhookAdminContext: true, + unstable_newEmbeddedAuthStrategy: true, } as const; const TEST_CONFIG = { diff --git a/packages/shopify-app-remix/src/server/authenticate/admin/__tests__/admin-client.test.ts b/packages/shopify-app-remix/src/server/authenticate/admin/__tests__/admin-client.test.ts index 8d189ce97b..37307ef2a5 100644 --- a/packages/shopify-app-remix/src/server/authenticate/admin/__tests__/admin-client.test.ts +++ b/packages/shopify-app-remix/src/server/authenticate/admin/__tests__/admin-client.test.ts @@ -11,7 +11,6 @@ import { APP_URL, BASE64_HOST, TEST_SHOP, - expectExitIframeRedirect, getJwt, getThrownResponse, setUpValidSession, @@ -21,7 +20,6 @@ import { expectAdminApiClient, } from '../../../__test-helpers'; import {shopifyApp} from '../../..'; -import {REAUTH_URL_HEADER} from '../../const'; import {AdminApiContext} from '../../../clients'; describe('admin.authenticate context', () => { @@ -102,25 +100,6 @@ describe('admin.authenticate context', () => { ])( '$testGroup re-authentication', ({testGroup: _testGroup, mockRequest, action}) => { - it('redirects to auth when request receives a 401 response and not embedded', async () => { - // GIVEN - const {admin, session} = await setUpNonEmbeddedFlow(); - const requestMock = await mockRequest(401); - - // WHEN - const response = await getThrownResponse( - async () => action(admin, session), - requestMock, - ); - - // THEN - expect(response.status).toEqual(302); - - const {hostname, pathname} = new URL(response.headers.get('Location')!); - expect(hostname).toEqual(TEST_SHOP); - expect(pathname).toEqual('/admin/oauth/authorize'); - }); - it('throws a response when request receives a non-401 response and not embedded', async () => { // GIVEN const {admin, session} = await setUpNonEmbeddedFlow(); @@ -135,43 +114,6 @@ describe('admin.authenticate context', () => { // THEN expect(response.status).toEqual(403); }); - - it('redirects to exit iframe when request receives a 401 response and embedded', async () => { - // GIVEN - const {admin, session} = await setUpEmbeddedFlow(); - const requestMock = await mockRequest(401); - - // WHEN - const response = await getThrownResponse( - async () => action(admin, session), - requestMock, - ); - - // THEN - expectExitIframeRedirect(response); - }); - - it('returns app bridge redirection headers when request receives a 401 response on fetch requests', async () => { - // GIVEN - const {admin, session} = await setUpFetchFlow(); - const requestMock = await mockRequest(401); - - // WHEN - const response = await getThrownResponse( - async () => action(admin, session), - requestMock, - ); - - // THEN - expect(response.status).toEqual(401); - - const {origin, pathname, searchParams} = new URL( - response.headers.get(REAUTH_URL_HEADER)!, - ); - expect(origin).toEqual(APP_URL); - expect(pathname).toEqual('/auth'); - expect(searchParams.get('shop')).toEqual(TEST_SHOP); - }); }, ); }); @@ -192,21 +134,6 @@ async function setUpEmbeddedFlow() { }; } -async function setUpFetchFlow() { - const shopify = shopifyApp(testConfig({restResources})); - await setUpValidSession(shopify.sessionStorage); - - const {token} = getJwt(); - const request = new Request(APP_URL, { - headers: {Authorization: `Bearer ${token}`}, - }); - - return { - shopify, - ...(await shopify.authenticate.admin(request)), - }; -} - async function setUpNonEmbeddedFlow() { const shopify = shopifyApp(testConfig({restResources, isEmbeddedApp: false})); const session = await setUpValidSession(shopify.sessionStorage); diff --git a/packages/shopify-app-remix/src/server/authenticate/admin/__tests__/doc-request-path.test.ts b/packages/shopify-app-remix/src/server/authenticate/admin/__tests__/doc-request-path.test.ts index d5735c93b2..8f98ff3391 100644 --- a/packages/shopify-app-remix/src/server/authenticate/admin/__tests__/doc-request-path.test.ts +++ b/packages/shopify-app-remix/src/server/authenticate/admin/__tests__/doc-request-path.test.ts @@ -128,4 +128,24 @@ describe('authorize.admin doc request path', () => { // THEN expect(response.status).toBe(400); }); + + it("redirects to the embedded app URL if the app isn't embedded yet", async () => { + // GIVEN + const config = testConfig(); + const shopify = shopifyApp(config); + await setUpValidSession(shopify.sessionStorage); + + // WHEN + const response = await getThrownResponse( + shopify.authenticate.admin, + new Request(`${APP_URL}?shop=${TEST_SHOP}&host=${BASE64_HOST}`), + ); + + // THEN + const {hostname, pathname} = new URL(response.headers.get('location')!); + + expect(response.status).toBe(302); + expect(hostname).toBe(SHOPIFY_HOST); + expect(pathname).toBe(`/apps/${API_KEY}`); + }); }); diff --git a/packages/shopify-app-remix/src/server/authenticate/admin/__tests__/session-token-header-path.test.ts b/packages/shopify-app-remix/src/server/authenticate/admin/__tests__/session-token-header-path.test.ts index 06dac552c6..4c582fce57 100644 --- a/packages/shopify-app-remix/src/server/authenticate/admin/__tests__/session-token-header-path.test.ts +++ b/packages/shopify-app-remix/src/server/authenticate/admin/__tests__/session-token-header-path.test.ts @@ -30,59 +30,6 @@ describe('authorize.session token header path', () => { // THEN expect(response.status).toBe(401); }); - - describe.each([true, false])('when isOnline: %s', (isOnline) => { - it(`returns app bridge redirection headers if there is no session`, async () => { - // GIVEN - const shopify = shopifyApp(testConfig({useOnlineTokens: isOnline})); - - // WHEN - const {token} = getJwt(); - const response = await getThrownResponse( - shopify.authenticate.admin, - new Request(`${APP_URL}?shop=${TEST_SHOP}&host=${BASE64_HOST}`, { - headers: {Authorization: `Bearer ${token}`}, - }), - ); - - // THEN - const {origin, pathname, searchParams} = new URL( - response.headers.get(REAUTH_URL_HEADER)!, - ); - - expect(response.status).toBe(401); - expect(origin).toBe(APP_URL); - expect(pathname).toBe('/auth'); - expect(searchParams.get('shop')).toBe(TEST_SHOP); - }); - - it(`returns app bridge redirection headers if the session is no longer valid`, async () => { - // GIVEN - const shopify = shopifyApp( - testConfig({useOnlineTokens: isOnline, scopes: ['otherTestScope']}), - ); - // The session scopes don't match the configured scopes, so it needs to be reset - await setUpValidSession(shopify.sessionStorage, isOnline); - - // WHEN - const {token} = getJwt(); - const response = await getThrownResponse( - shopify.authenticate.admin, - new Request(`${APP_URL}?shop=${TEST_SHOP}&host=${BASE64_HOST}`, { - headers: {Authorization: `Bearer ${token}`}, - }), - ); - - // THEN - const {origin, pathname, searchParams} = new URL( - response.headers.get(REAUTH_URL_HEADER)!, - ); - expect(response.status).toBe(401); - expect(origin).toBe(APP_URL); - expect(pathname).toBe('/auth'); - expect(searchParams.get('shop')).toBe(TEST_SHOP); - }); - }); }); describe.each([true, false])( @@ -92,10 +39,9 @@ describe('authorize.session token header path', () => { // GIVEN const shopify = shopifyApp(testConfig({useOnlineTokens: isOnline})); - const testSession = await setUpValidSession( - shopify.sessionStorage, + const testSession = await setUpValidSession(shopify.sessionStorage, { isOnline, - ); + }); // WHEN const {token, payload} = getJwt(); @@ -120,7 +66,9 @@ describe('authorize.session token header path', () => { let testSession: Session; testSession = await setUpValidSession(shopify.sessionStorage); if (isOnline) { - testSession = await setUpValidSession(shopify.sessionStorage, true); + testSession = await setUpValidSession(shopify.sessionStorage, { + isOnline: true, + }); } // WHEN diff --git a/packages/shopify-app-remix/src/server/authenticate/admin/authenticate.ts b/packages/shopify-app-remix/src/server/authenticate/admin/authenticate.ts index 87689cd97f..8782e8ca1e 100644 --- a/packages/shopify-app-remix/src/server/authenticate/admin/authenticate.ts +++ b/packages/shopify-app-remix/src/server/authenticate/admin/authenticate.ts @@ -30,7 +30,14 @@ import { renderAppBridge, validateShopAndHostParams, } from './helpers'; -import {AuthorizationStrategy, SessionTokenContext} from './strategies/types'; +import {AuthorizationStrategy} from './strategies/types'; + +export interface SessionTokenContext { + shop: string; + sessionId?: string; + sessionToken?: string; + payload?: JwtPayload; +} interface AuthStrategyParams extends BasicParams { strategy: AuthorizationStrategy; @@ -68,16 +75,17 @@ export function authStrategyFactory< function createContext( request: Request, session: Session, + authStrategy: AuthorizationStrategy, sessionToken?: JwtPayload, ): AdminContext { const context: | EmbeddedAdminContext | NonEmbeddedAdminContext = { - admin: createAdminApiContext(request, session, { - api, - logger, - config, - }), + admin: createAdminApiContext( + session, + params, + authStrategy.handleClientError(request), + ), billing: { require: requireBillingFactory(params, request, session), request: requestBillingFactory(params, request, session), @@ -116,28 +124,26 @@ export function authStrategyFactory< logger.info('Authenticating admin request'); - const {payload, shop, sessionId} = await getSessionTokenContext( - params, - request, - ); + const {payload, shop, sessionId, sessionToken} = + await getSessionTokenContext(params, request); logger.debug('Loading session from storage', {sessionId}); const existingSession = sessionId ? await config.sessionStorage.loadSession(sessionId) : undefined; - const session = await strategy.authenticate( - request, - existingSession, + const session = await strategy.authenticate(request, { + session: existingSession, + sessionToken, shop, - ); + }); logger.debug('Request is valid, loaded session from session token', { shop: session.shop, isOnline: session.isOnline, }); - return createContext(request, session, payload); + return createContext(request, session, strategy, payload); } catch (errorOrResponse) { if (errorOrResponse instanceof Response) { ensureCORSHeadersFactory(params, request)(errorOrResponse); @@ -159,10 +165,10 @@ async function getSessionTokenContext( const sessionToken = (headerSessionToken || searchParamSessionToken)!; logger.debug('Attempting to authenticate session token', { - sessionToken: { + sessionToken: JSON.stringify({ header: headerSessionToken, search: searchParamSessionToken, - }, + }), }); if (config.isEmbeddedApp) { @@ -178,7 +184,7 @@ async function getSessionTokenContext( ? api.session.getJwtSessionId(shop, payload.sub) : api.session.getOfflineId(shop); - return {shop, payload, sessionId}; + return {shop, payload, sessionId, sessionToken}; } const url = new URL(request.url); @@ -189,5 +195,5 @@ async function getSessionTokenContext( rawRequest: request, }); - return {shop, sessionId, payload: undefined}; + return {shop, sessionId, payload: undefined, sessionToken}; } diff --git a/packages/shopify-app-remix/src/server/authenticate/admin/helpers/create-admin-api-context.ts b/packages/shopify-app-remix/src/server/authenticate/admin/helpers/create-admin-api-context.ts index 3699bc810d..00caf4bb87 100644 --- a/packages/shopify-app-remix/src/server/authenticate/admin/helpers/create-admin-api-context.ts +++ b/packages/shopify-app-remix/src/server/authenticate/admin/helpers/create-admin-api-context.ts @@ -1,22 +1,22 @@ import {Session, ShopifyRestResources} from '@shopify/shopify-api'; import type {BasicParams} from '../../../types'; -import {AdminApiContext, adminClientFactory} from '../../../clients/admin'; - -import {handleClientErrorFactory} from './handle-client-error'; +import { + AdminApiContext, + HandleAdminClientError, + adminClientFactory, +} from '../../../clients/admin'; export function createAdminApiContext< Resources extends ShopifyRestResources = ShopifyRestResources, >( - request: Request, session: Session, params: BasicParams, + handleClientError: HandleAdminClientError, ): AdminApiContext { return adminClientFactory({ session, params, - handleClientError: handleClientErrorFactory({ - request, - }), + handleClientError, }); } diff --git a/packages/shopify-app-remix/src/server/authenticate/admin/helpers/handle-client-error.ts b/packages/shopify-app-remix/src/server/authenticate/admin/helpers/handle-client-error.ts index 1661533fb4..fda47f8092 100644 --- a/packages/shopify-app-remix/src/server/authenticate/admin/helpers/handle-client-error.ts +++ b/packages/shopify-app-remix/src/server/authenticate/admin/helpers/handle-client-error.ts @@ -1,15 +1,11 @@ import {HttpResponseError} from '@shopify/shopify-api'; import type {HandleAdminClientError} from '../../../clients/admin/types'; - -import {redirectToAuthPage} from './redirect-to-auth-page'; - -interface HandleClientErrorOptions { - request: Request; -} +import {HandleClientErrorOptions} from '../strategies/types'; export function handleClientErrorFactory({ request, + onError, }: HandleClientErrorOptions): HandleAdminClientError { return async function handleClientError({ error, @@ -32,8 +28,8 @@ export function handleClientErrorFactory({ }, ); - if (error.response.code === 401) { - throw await redirectToAuthPage(params, request, session.shop); + if (onError) { + await onError({request, session, error}); } // forward a minimal copy of the upstream HTTP response instead of an Error: diff --git a/packages/shopify-app-remix/src/server/authenticate/admin/helpers/redirect-to-bounce-page.ts b/packages/shopify-app-remix/src/server/authenticate/admin/helpers/redirect-to-bounce-page.ts index 9797c34af2..0ba5c46ffc 100644 --- a/packages/shopify-app-remix/src/server/authenticate/admin/helpers/redirect-to-bounce-page.ts +++ b/packages/shopify-app-remix/src/server/authenticate/admin/helpers/redirect-to-bounce-page.ts @@ -7,13 +7,17 @@ export const redirectToBouncePage = (params: BasicParams, url: URL): never => { // Make sure we always point to the configured app URL so it also works behind reverse proxies (that alter the Host // header). - url.searchParams.set( + const searchParams = url.searchParams; + searchParams.delete('id_token'); + searchParams.set( 'shopify-reload', - `${config.appUrl}${url.pathname}${url.search}`, + `${config.appUrl}${url.pathname}?${searchParams.toString()}`, ); // eslint-disable-next-line no-warning-comments // TODO Make sure this works on chrome without a tunnel (weird HTTPS redirect issue) // https://github.com/orgs/Shopify/projects/6899/views/1?pane=issue&itemId=28376650 - throw redirect(`${config.auth.patchSessionTokenPath}${url.search}`); + throw redirect( + `${config.auth.patchSessionTokenPath}?${searchParams.toString()}`, + ); }; diff --git a/packages/shopify-app-remix/src/server/authenticate/admin/helpers/redirect.ts b/packages/shopify-app-remix/src/server/authenticate/admin/helpers/redirect.ts index 2de7cb8aa9..49d678215b 100644 --- a/packages/shopify-app-remix/src/server/authenticate/admin/helpers/redirect.ts +++ b/packages/shopify-app-remix/src/server/authenticate/admin/helpers/redirect.ts @@ -66,15 +66,11 @@ function isBounceRequest(request: Request) { } function isDataRequest(request: Request) { - const {searchParams} = new URL(request.url); - const isGet = request.method === 'GET'; const sessionTokenHeader = Boolean(getSessionTokenHeader(request)); - const sessionTokenSearchParam = searchParams.has('id_token'); return ( sessionTokenHeader && - !sessionTokenSearchParam && !isBounceRequest(request) && (!isEmbeddedRequest(request) || !isGet) ); diff --git a/packages/shopify-app-remix/src/server/authenticate/admin/helpers/trigger-after-auth-hook.ts b/packages/shopify-app-remix/src/server/authenticate/admin/helpers/trigger-after-auth-hook.ts index 18eb238738..b27dd3f1a2 100644 --- a/packages/shopify-app-remix/src/server/authenticate/admin/helpers/trigger-after-auth-hook.ts +++ b/packages/shopify-app-remix/src/server/authenticate/admin/helpers/trigger-after-auth-hook.ts @@ -1,18 +1,28 @@ import {Session, ShopifyRestResources} from '@shopify/shopify-api'; import type {BasicParams} from '../../../types'; +import {AuthorizationStrategy} from '../strategies/types'; import {createAdminApiContext} from './create-admin-api-context'; export async function triggerAfterAuthHook< Resources extends ShopifyRestResources = ShopifyRestResources, ->(params: BasicParams, session: Session, request: Request) { +>( + params: BasicParams, + session: Session, + request: Request, + authStrategy: AuthorizationStrategy, +) { const {config, logger} = params; if (config.hooks.afterAuth) { logger.info('Running afterAuth hook'); await config.hooks.afterAuth({ session, - admin: createAdminApiContext(request, session, params), + admin: createAdminApiContext( + session, + params, + authStrategy.handleClientError(request), + ), }); } } diff --git a/packages/shopify-app-remix/src/server/authenticate/admin/strategies/__tests__/auth-code-flow/admin-client.test.ts b/packages/shopify-app-remix/src/server/authenticate/admin/strategies/__tests__/auth-code-flow/admin-client.test.ts new file mode 100644 index 0000000000..729e37f67f --- /dev/null +++ b/packages/shopify-app-remix/src/server/authenticate/admin/strategies/__tests__/auth-code-flow/admin-client.test.ts @@ -0,0 +1,222 @@ +import { + ApiVersion, + LATEST_API_VERSION, + SESSION_COOKIE_NAME, + Session, +} from '@shopify/shopify-api'; +import {restResources} from '@shopify/shopify-api/rest/admin/2023-04'; + +import { + APP_URL, + BASE64_HOST, + TEST_SHOP, + expectExitIframeRedirect, + getJwt, + getThrownResponse, + setUpValidSession, + signRequestCookie, + testConfig, + mockExternalRequest, + expectAdminApiClient, +} from '../../../../../__test-helpers'; +import {shopifyApp} from '../../../../..'; +import {REAUTH_URL_HEADER} from '../../../../const'; +import {AdminApiContext} from '../../../../../clients'; + +describe('admin.authenticate context', () => { + expectAdminApiClient(async () => { + const { + admin, + expectedSession, + session: actualSession, + } = await setUpEmbeddedFlow(); + + return {admin, expectedSession, actualSession}; + }); + describe.each([ + { + testGroup: 'REST client', + mockRequest: mockRestRequest, + action: async (admin: AdminApiContext, _session: Session) => + admin.rest.get({path: '/customers.json'}), + }, + { + testGroup: 'REST resources', + mockRequest: mockRestRequest, + action: async (admin: AdminApiContext, session: Session) => + admin.rest.resources.Customer.all({session}), + }, + { + testGroup: 'GraphQL client', + mockRequest: mockGraphqlRequest(), + action: async (admin: AdminApiContext, _session: Session) => + admin.graphql('{ shop { name } }'), + }, + { + testGroup: 'GraphQL client with options', + mockRequest: mockGraphqlRequest('2021-01' as ApiVersion), + action: async (admin: AdminApiContext, _session: Session) => + admin.graphql( + 'mutation myMutation($ID: String!) { shop(ID: $ID) { name } }', + { + variables: {ID: '123'}, + apiVersion: '2021-01' as ApiVersion, + headers: {custom: 'header'}, + tries: 2, + }, + ), + }, + ])( + '$testGroup re-authentication', + ({testGroup: _testGroup, mockRequest, action}) => { + it('redirects to auth when request receives a 401 response and not embedded', async () => { + // GIVEN + const {admin, session} = await setUpNonEmbeddedFlow(); + const requestMock = await mockRequest(401); + + // WHEN + const response = await getThrownResponse( + async () => action(admin, session), + requestMock, + ); + + // THEN + expect(response.status).toEqual(302); + + const {hostname, pathname} = new URL(response.headers.get('Location')!); + expect(hostname).toEqual(TEST_SHOP); + expect(pathname).toEqual('/admin/oauth/authorize'); + }); + + it('redirects to exit iframe when request receives a 401 response and embedded', async () => { + // GIVEN + const {admin, session} = await setUpEmbeddedFlow(); + const requestMock = await mockRequest(401); + + // WHEN + const response = await getThrownResponse( + async () => action(admin, session), + requestMock, + ); + + // THEN + expectExitIframeRedirect(response); + }); + + it('returns app bridge redirection headers when request receives a 401 response on fetch requests', async () => { + // GIVEN + const {admin, session} = await setUpFetchFlow(); + const requestMock = await mockRequest(401); + + // WHEN + const response = await getThrownResponse( + async () => action(admin, session), + requestMock, + ); + + // THEN + expect(response.status).toEqual(401); + + const {origin, pathname, searchParams} = new URL( + response.headers.get(REAUTH_URL_HEADER)!, + ); + expect(origin).toEqual(APP_URL); + expect(pathname).toEqual('/auth'); + expect(searchParams.get('shop')).toEqual(TEST_SHOP); + }); + }, + ); +}); + +async function setUpEmbeddedFlow() { + const shopify = shopifyApp( + testConfig({ + future: {unstable_newEmbeddedAuthStrategy: false}, + restResources, + }), + ); + const expectedSession = await setUpValidSession(shopify.sessionStorage); + + const {token} = getJwt(); + const request = new Request( + `${APP_URL}?embedded=1&shop=${TEST_SHOP}&host=${BASE64_HOST}&id_token=${token}`, + ); + + return { + shopify, + expectedSession, + ...(await shopify.authenticate.admin(request)), + }; +} + +async function setUpFetchFlow() { + const shopify = shopifyApp( + testConfig({ + future: {unstable_newEmbeddedAuthStrategy: false}, + restResources, + }), + ); + await setUpValidSession(shopify.sessionStorage); + + const {token} = getJwt(); + const request = new Request(APP_URL, { + headers: {Authorization: `Bearer ${token}`}, + }); + + return { + shopify, + ...(await shopify.authenticate.admin(request)), + }; +} + +async function setUpNonEmbeddedFlow() { + const shopify = shopifyApp( + testConfig({ + future: {unstable_newEmbeddedAuthStrategy: false}, + restResources, + isEmbeddedApp: false, + }), + ); + const session = await setUpValidSession(shopify.sessionStorage); + + const request = new Request(`${APP_URL}?shop=${TEST_SHOP}`); + signRequestCookie({ + request, + cookieName: SESSION_COOKIE_NAME, + cookieValue: session.id, + }); + + return { + shopify, + ...(await shopify.authenticate.admin(request)), + }; +} + +async function mockRestRequest(status) { + const requestMock = new Request( + `https://${TEST_SHOP}/admin/api/${LATEST_API_VERSION}/customers.json`, + ); + + await mockExternalRequest({ + request: requestMock, + response: new Response('{}', {status}), + }); + + return requestMock; +} + +function mockGraphqlRequest(apiVersion = LATEST_API_VERSION) { + return async function (status = 401) { + const requestMock = new Request( + `https://${TEST_SHOP}/admin/api/${apiVersion}/graphql.json`, + {method: 'POST'}, + ); + + await mockExternalRequest({ + request: requestMock, + response: new Response(undefined, {status}), + }); + + return requestMock; + }; +} diff --git a/packages/shopify-app-remix/src/server/authenticate/admin/strategies/__tests__/auth-code-flow/auth-callback-path.test.ts b/packages/shopify-app-remix/src/server/authenticate/admin/strategies/__tests__/auth-code-flow/auth-callback-path.test.ts new file mode 100644 index 0000000000..fac4593afd --- /dev/null +++ b/packages/shopify-app-remix/src/server/authenticate/admin/strategies/__tests__/auth-code-flow/auth-callback-path.test.ts @@ -0,0 +1,396 @@ +import {HashFormat, createSHA256HMAC} from '@shopify/shopify-api/runtime'; + +import {shopifyApp} from '../../../../..'; +import { + BASE64_HOST, + TEST_SHOP, + getThrownResponse, + signRequestCookie, + testConfig, + mockExternalRequest, + APP_URL, +} from '../../../../../__test-helpers'; + +describe('authorize.admin auth callback path', () => { + describe.each([true, false])('when isEmbeddedApp: %s', (isEmbeddedApp) => { + describe('errors', () => { + test('throws an error if the shop param is missing', async () => { + // GIVEN + const config = testConfig({ + future: {unstable_newEmbeddedAuthStrategy: !isEmbeddedApp}, + isEmbeddedApp, + }); + const shopify = shopifyApp(config); + + // WHEN + const response = await getThrownResponse( + shopify.authenticate.admin, + new Request(getCallbackUrl(config)), + ); + + // THEN + expect(response.status).toBe(400); + expect(await response.text()).toBe('Shop param is invalid'); + }); + + test('throws an error if the shop param is not valid', async () => { + // GIVEN + const config = testConfig({ + future: {unstable_newEmbeddedAuthStrategy: !isEmbeddedApp}, + isEmbeddedApp, + }); + const shopify = shopifyApp(config); + + // WHEN + const response = await getThrownResponse( + shopify.authenticate.admin, + new Request(`${getCallbackUrl(config)}?shop=invalid`), + ); + + // THEN + expect(response.status).toBe(400); + expect(await response.text()).toBe('Shop param is invalid'); + }); + + test('throws an 302 Response to begin auth if CookieNotFound error', async () => { + // GIVEN + const config = testConfig({ + future: {unstable_newEmbeddedAuthStrategy: !isEmbeddedApp}, + isEmbeddedApp, + }); + const shopify = shopifyApp(config); + + // WHEN + const callbackUrl = getCallbackUrl(config); + const response = await getThrownResponse( + shopify.authenticate.admin, + new Request(`${callbackUrl}?shop=${TEST_SHOP}`), + ); + + // THEN + const {searchParams, hostname} = new URL( + response.headers.get('location')!, + ); + + expect(response.status).toBe(302); + expect(hostname).toBe(TEST_SHOP); + expect(searchParams.get('client_id')).toBe(config.apiKey); + expect(searchParams.get('scope')).toBe(config.scopes!.toString()); + expect(searchParams.get('redirect_uri')).toBe(callbackUrl); + expect(searchParams.get('state')).toStrictEqual(expect.any(String)); + }); + + test('throws a 400 if there is no HMAC param', async () => { + // GIVEN + const config = testConfig({ + future: {unstable_newEmbeddedAuthStrategy: !isEmbeddedApp}, + isEmbeddedApp, + }); + const shopify = shopifyApp(config); + + // WHEN + const state = 'nonce'; + const request = new Request( + `${getCallbackUrl(config)}?shop=${TEST_SHOP}&state=${state}`, + ); + + signRequestCookie({ + request, + cookieName: 'shopify_app_state', + cookieValue: state, + }); + + const response = await getThrownResponse( + shopify.authenticate.admin, + request, + ); + + // THEN + expect(response.status).toBe(400); + expect(response.statusText).toBe('Invalid OAuth Request'); + }); + + test('throws a 400 if the HMAC param is invalid', async () => { + // GIVEN + const config = testConfig({ + future: {unstable_newEmbeddedAuthStrategy: !isEmbeddedApp}, + isEmbeddedApp, + }); + const shopify = shopifyApp(config); + + // WHEN + const state = 'nonce'; + const request = new Request( + `${getCallbackUrl( + config, + )}?shop=${TEST_SHOP}&state=${state}&hmac=invalid`, + ); + + signRequestCookie({ + request, + cookieName: 'shopify_app_state', + cookieValue: state, + }); + + const response = await getThrownResponse( + shopify.authenticate.admin, + request, + ); + + // THEN + expect(response.status).toBe(400); + }); + + test('throws a 500 if any other errors are thrown', async () => { + // GIVEN + const config = testConfig({ + future: {unstable_newEmbeddedAuthStrategy: !isEmbeddedApp}, + isEmbeddedApp, + }); + const shopify = shopifyApp({ + ...config, + hooks: { + afterAuth: () => { + throw new Error('test'); + }, + }, + }); + + // WHEN + await mockCodeExchangeRequest('offline'); + const response = await getThrownResponse( + shopify.authenticate.admin, + await getValidCallbackRequest(config), + ); + + // THEN + expect(response.status).toBe(500); + }); + }); + + describe('Success states', () => { + test('Exchanges the code for a token and saves it to SessionStorage', async () => { + // GIVEN + const config = testConfig({ + future: {unstable_newEmbeddedAuthStrategy: !isEmbeddedApp}, + isEmbeddedApp, + }); + const shopify = shopifyApp(config); + + // WHEN + await mockCodeExchangeRequest('offline'); + const response = await getThrownResponse( + shopify.authenticate.admin, + await getValidCallbackRequest(config), + ); + + // THEN + const [session] = await config.sessionStorage!.findSessionsByShop( + TEST_SHOP, + ); + + expect(session).toMatchObject({ + accessToken: '123abc', + id: `offline_${TEST_SHOP}`, + isOnline: false, + scope: 'read_products', + shop: TEST_SHOP, + state: 'nonce', + }); + }); + + test('throws an 302 Response to begin auth if token was offline and useOnlineTokens is true', async () => { + // GIVEN + const config = testConfig({ + useOnlineTokens: true, + future: {unstable_newEmbeddedAuthStrategy: !isEmbeddedApp}, + isEmbeddedApp, + }); + const shopify = shopifyApp(config); + + // WHEN + await mockCodeExchangeRequest('offline'); + const response = await getThrownResponse( + shopify.authenticate.admin, + await getValidCallbackRequest(config), + ); + + // THEN + const {searchParams, hostname} = new URL( + response.headers.get('location')!, + ); + + expect(response.status).toBe(302); + expect(hostname).toBe(TEST_SHOP); + expect(searchParams.get('client_id')).toBe(config.apiKey); + expect(searchParams.get('scope')).toBe(config.scopes!.toString()); + expect(searchParams.get('redirect_uri')).toBe(getCallbackUrl(config)); + expect(searchParams.get('state')).toStrictEqual(expect.any(String)); + }); + + test('Does not throw a 302 Response to begin auth if token was online', async () => { + // GIVEN + const config = testConfig({ + useOnlineTokens: true, + future: {unstable_newEmbeddedAuthStrategy: !isEmbeddedApp}, + isEmbeddedApp, + }); + const shopify = shopifyApp(config); + + // WHEN + await mockCodeExchangeRequest('online'); + const response = await getThrownResponse( + shopify.authenticate.admin, + await getValidCallbackRequest(config), + ); + + // THEN + const base = `http://${APP_URL}`; + const {hostname} = new URL(response.headers.get('location')!, base); + expect(hostname).not.toBe(TEST_SHOP); + }); + + test('Runs the afterAuth hooks passing', async () => { + // GIVEN + const afterAuthMock = jest.fn(); + const config = testConfig({ + hooks: { + afterAuth: afterAuthMock, + }, + future: {unstable_newEmbeddedAuthStrategy: !isEmbeddedApp}, + isEmbeddedApp, + }); + const shopify = shopifyApp(config); + + // WHEN + await mockCodeExchangeRequest(); + const response = await getThrownResponse( + shopify.authenticate.admin, + await getValidCallbackRequest(config), + ); + + // THEN + expect(afterAuthMock).toHaveBeenCalledTimes(1); + }); + + if (isEmbeddedApp) { + test('throws a 302 response to the embedded app URL', async () => { + // GIVEN + const config = testConfig({ + future: {unstable_newEmbeddedAuthStrategy: false}, + isEmbeddedApp: true, + }); + const shopify = shopifyApp(config); + + // WHEN + await mockCodeExchangeRequest('offline'); + const response = await getThrownResponse( + shopify.authenticate.admin, + await getValidCallbackRequest(config), + ); + + // THEN + expect(response.status).toBe(302); + expect(response.headers.get('location')).toBe( + 'https://totally-real-host.myshopify.io/apps/testApiKey', + ); + }); + } else { + test('throws a 302 to /', async () => { + // GIVEN + const config = testConfig({ + isEmbeddedApp: false, + }); + const shopify = shopifyApp(config); + + // WHEN + await mockCodeExchangeRequest('offline'); + const request = await getValidCallbackRequest(config); + const response = await getThrownResponse( + shopify.authenticate.admin, + request, + ); + + // THEN + const url = new URL(request.url); + const host = url.searchParams.get('host'); + expect(response.status).toBe(302); + expect(response.headers.get('location')).toBe( + `/?shop=${TEST_SHOP}&host=${host}`, + ); + expect(response.headers.get('set-cookie')).toBe( + [ + 'shopify_app_state=;path=/;expires=Thu, 01 Jan 1970 00:00:00 GMT', + `shopify_app_session=offline_${TEST_SHOP};sameSite=lax; secure=true; path=/`, + 'shopify_app_session.sig=0qSrbSUpq8Cr+fev917WGyO1IU3Py1fTwZukcHd4hVE=;sameSite=lax; secure=true; path=/', + ].join(', '), + ); + }); + } + }); + }); +}); + +function getCallbackUrl(appConfig: ReturnType) { + return `${appConfig.appUrl}/auth/callback`; +} + +async function getValidCallbackRequest(config: ReturnType) { + const cookieName = 'shopify_app_state'; + const state = 'nonce'; + const code = 'code_from_shopify'; + const now = Math.trunc(Date.now() / 1000) - 2; + const queryParams = `code=${code}&host=${BASE64_HOST}&shop=${TEST_SHOP}&state=${state}×tamp=${now}`; + const hmac = await createSHA256HMAC( + config.apiSecretKey, + queryParams, + HashFormat.Hex, + ); + + const request = new Request( + `${getCallbackUrl(config)}?${queryParams}&hmac=${hmac}`, + ); + + signRequestCookie({ + request, + cookieName, + cookieValue: state, + }); + + return request; +} + +async function mockCodeExchangeRequest( + tokenType: 'online' | 'offline' = 'offline', +) { + const responseBody = { + access_token: '123abc', + scope: 'read_products', + }; + + await mockExternalRequest({ + request: new Request(`https://${TEST_SHOP}/admin/oauth/access_token`, { + method: 'POST', + }), + response: + tokenType === 'offline' + ? new Response(JSON.stringify(responseBody)) + : new Response( + JSON.stringify({ + ...responseBody, + expires_in: Math.trunc(Date.now() / 1000) + 3600, + associated_user_scope: 'read_products', + associated_user: { + id: 902541635, + first_name: 'John', + last_name: 'Smith', + email: 'john@example.com', + email_verified: true, + account_owner: true, + locale: 'en', + collaborator: false, + }, + }), + ), + }); +} diff --git a/packages/shopify-app-remix/src/server/authenticate/admin/strategies/__tests__/auth-code-flow/auth-code-flow/auth-callback-path.test.ts b/packages/shopify-app-remix/src/server/authenticate/admin/strategies/__tests__/auth-code-flow/auth-code-flow/auth-callback-path.test.ts deleted file mode 100644 index 2d637d4833..0000000000 --- a/packages/shopify-app-remix/src/server/authenticate/admin/strategies/__tests__/auth-code-flow/auth-code-flow/auth-callback-path.test.ts +++ /dev/null @@ -1,356 +0,0 @@ -import {HashFormat, createSHA256HMAC} from '@shopify/shopify-api/runtime'; - -import {shopifyApp} from '../../../../../..'; -import { - BASE64_HOST, - TEST_SHOP, - getThrownResponse, - signRequestCookie, - testConfig, - mockExternalRequest, -} from '../../../../../../__test-helpers'; - -describe('authorize.admin auth callback path', () => { - describe('errors', () => { - test('throws an error if the shop param is missing', async () => { - // GIVEN - const config = testConfig(); - const shopify = shopifyApp(config); - - // WHEN - const response = await getThrownResponse( - shopify.authenticate.admin, - new Request(getCallbackUrl(config)), - ); - - // THEN - expect(response.status).toBe(400); - expect(await response.text()).toBe('Shop param is invalid'); - }); - - test('throws an error if the shop param is not valid', async () => { - // GIVEN - const config = testConfig(); - const shopify = shopifyApp(config); - - // WHEN - const response = await getThrownResponse( - shopify.authenticate.admin, - new Request(`${getCallbackUrl(config)}?shop=invalid`), - ); - - // THEN - expect(response.status).toBe(400); - expect(await response.text()).toBe('Shop param is invalid'); - }); - - test('throws an 302 Response to begin auth if CookieNotFound error', async () => { - // GIVEN - const config = testConfig(); - const shopify = shopifyApp(config); - - // WHEN - const callbackUrl = getCallbackUrl(config); - const response = await getThrownResponse( - shopify.authenticate.admin, - new Request(`${callbackUrl}?shop=${TEST_SHOP}`), - ); - - // THEN - const {searchParams, hostname} = new URL( - response.headers.get('location')!, - ); - - expect(response.status).toBe(302); - expect(hostname).toBe(TEST_SHOP); - expect(searchParams.get('client_id')).toBe(config.apiKey); - expect(searchParams.get('scope')).toBe(config.scopes!.toString()); - expect(searchParams.get('redirect_uri')).toBe(callbackUrl); - expect(searchParams.get('state')).toStrictEqual(expect.any(String)); - }); - - test('throws a 400 if there is no HMAC param', async () => { - // GIVEN - const config = testConfig(); - const shopify = shopifyApp(config); - - // WHEN - const state = 'nonce'; - const request = new Request( - `${getCallbackUrl(config)}?shop=${TEST_SHOP}&state=${state}`, - ); - - signRequestCookie({ - request, - cookieName: 'shopify_app_state', - cookieValue: state, - }); - - const response = await getThrownResponse( - shopify.authenticate.admin, - request, - ); - - // THEN - expect(response.status).toBe(400); - expect(response.statusText).toBe('Invalid OAuth Request'); - }); - - test('throws a 400 if the HMAC param is invalid', async () => { - // GIVEN - const config = testConfig(); - const shopify = shopifyApp(config); - - // WHEN - const state = 'nonce'; - const request = new Request( - `${getCallbackUrl( - config, - )}?shop=${TEST_SHOP}&state=${state}&hmac=invalid`, - ); - - signRequestCookie({ - request, - cookieName: 'shopify_app_state', - cookieValue: state, - }); - - const response = await getThrownResponse( - shopify.authenticate.admin, - request, - ); - - // THEN - expect(response.status).toBe(400); - }); - - test('throws a 500 if any other errors are thrown', async () => { - // GIVEN - const config = testConfig(); - const shopify = shopifyApp({ - ...config, - hooks: { - afterAuth: () => { - throw new Error('test'); - }, - }, - }); - - // WHEN - await mockCodeExchangeRequest('offline'); - const response = await getThrownResponse( - shopify.authenticate.admin, - await getValidCallbackRequest(config), - ); - - // THEN - expect(response.status).toBe(500); - }); - }); - - describe('Success states', () => { - test('Exchanges the code for a token and saves it to SessionStorage', async () => { - // GIVEN - const config = testConfig(); - const shopify = shopifyApp(config); - - // WHEN - await mockCodeExchangeRequest('offline'); - const response = await getThrownResponse( - shopify.authenticate.admin, - await getValidCallbackRequest(config), - ); - - // THEN - const [session] = await config.sessionStorage!.findSessionsByShop( - TEST_SHOP, - ); - - expect(session).toMatchObject({ - accessToken: '123abc', - id: `offline_${TEST_SHOP}`, - isOnline: false, - scope: 'read_products', - shop: TEST_SHOP, - state: 'nonce', - }); - }); - - test('throws an 302 Response to begin auth if token was offline and useOnlineTokens is true', async () => { - // GIVEN - const config = testConfig({useOnlineTokens: true}); - const shopify = shopifyApp(config); - - // WHEN - await mockCodeExchangeRequest('offline'); - const response = await getThrownResponse( - shopify.authenticate.admin, - await getValidCallbackRequest(config), - ); - - // THEN - const {searchParams, hostname} = new URL( - response.headers.get('location')!, - ); - - expect(response.status).toBe(302); - expect(hostname).toBe(TEST_SHOP); - expect(searchParams.get('client_id')).toBe(config.apiKey); - expect(searchParams.get('scope')).toBe(config.scopes!.toString()); - expect(searchParams.get('redirect_uri')).toBe(getCallbackUrl(config)); - expect(searchParams.get('state')).toStrictEqual(expect.any(String)); - }); - - test('Does not throw a 302 Response to begin auth if token was online', async () => { - // GIVEN - const config = testConfig({useOnlineTokens: true}); - const shopify = shopifyApp(config); - - // WHEN - await mockCodeExchangeRequest('online'); - const response = await getThrownResponse( - shopify.authenticate.admin, - await getValidCallbackRequest(config), - ); - - // THEN - const {hostname} = new URL(response.headers.get('location')!); - expect(hostname).not.toBe(TEST_SHOP); - }); - - test('Runs the afterAuth hooks passing', async () => { - // GIVEN - const afterAuthMock = jest.fn(); - const config = testConfig({ - hooks: { - afterAuth: afterAuthMock, - }, - }); - const shopify = shopifyApp(config); - - // WHEN - await mockCodeExchangeRequest(); - const response = await getThrownResponse( - shopify.authenticate.admin, - await getValidCallbackRequest(config), - ); - - // THEN - expect(afterAuthMock).toHaveBeenCalledTimes(1); - }); - - test('throws a 302 response to the emebdded app URL if isEmbeddedApp is true', async () => { - // GIVEN - const config = testConfig(); - const shopify = shopifyApp(config); - - // WHEN - await mockCodeExchangeRequest('offline'); - const response = await getThrownResponse( - shopify.authenticate.admin, - await getValidCallbackRequest(config), - ); - - // THEN - expect(response.status).toBe(302); - expect(response.headers.get('location')).toBe( - 'https://totally-real-host.myshopify.io/apps/testApiKey', - ); - }); - - test('throws a 302 to / if embedded is not true', async () => { - // GIVEN - const config = testConfig({ - isEmbeddedApp: false, - }); - const shopify = shopifyApp(config); - - // WHEN - await mockCodeExchangeRequest('offline'); - const request = await getValidCallbackRequest(config); - const response = await getThrownResponse( - shopify.authenticate.admin, - request, - ); - - // THEN - const url = new URL(request.url); - const host = url.searchParams.get('host'); - expect(response.status).toBe(302); - expect(response.headers.get('location')).toBe( - `/?shop=${TEST_SHOP}&host=${host}`, - ); - expect(response.headers.get('set-cookie')).toBe( - [ - 'shopify_app_state=;path=/;expires=Thu, 01 Jan 1970 00:00:00 GMT', - `shopify_app_session=offline_${TEST_SHOP};sameSite=lax; secure=true; path=/`, - 'shopify_app_session.sig=0qSrbSUpq8Cr+fev917WGyO1IU3Py1fTwZukcHd4hVE=;sameSite=lax; secure=true; path=/', - ].join(', '), - ); - }); - }); -}); - -function getCallbackUrl(appConfig: ReturnType) { - return `${appConfig.appUrl}/auth/callback`; -} - -async function getValidCallbackRequest(config: ReturnType) { - const cookieName = 'shopify_app_state'; - const state = 'nonce'; - const code = 'code_from_shopify'; - const now = Math.trunc(Date.now() / 1000) - 2; - const queryParams = `code=${code}&host=${BASE64_HOST}&shop=${TEST_SHOP}&state=${state}×tamp=${now}`; - const hmac = await createSHA256HMAC( - config.apiSecretKey, - queryParams, - HashFormat.Hex, - ); - - const request = new Request( - `${getCallbackUrl(config)}?${queryParams}&hmac=${hmac}`, - ); - - signRequestCookie({ - request, - cookieName, - cookieValue: state, - }); - - return request; -} - -async function mockCodeExchangeRequest( - tokenType: 'online' | 'offline' = 'offline', -) { - const responseBody = { - access_token: '123abc', - scope: 'read_products', - }; - - await mockExternalRequest({ - request: new Request(`https://${TEST_SHOP}/admin/oauth/access_token`, { - method: 'POST', - }), - response: - tokenType === 'offline' - ? new Response(JSON.stringify(responseBody)) - : new Response( - JSON.stringify({ - ...responseBody, - expires_in: Math.trunc(Date.now() / 1000) + 3600, - associated_user_scope: 'read_products', - associated_user: { - id: 902541635, - first_name: 'John', - last_name: 'Smith', - email: 'john@example.com', - email_verified: true, - account_owner: true, - locale: 'en', - collaborator: false, - }, - }), - ), - }); -} diff --git a/packages/shopify-app-remix/src/server/authenticate/admin/strategies/__tests__/auth-code-flow/auth-code-flow/auth-path.test.ts b/packages/shopify-app-remix/src/server/authenticate/admin/strategies/__tests__/auth-code-flow/auth-path.test.ts similarity index 78% rename from packages/shopify-app-remix/src/server/authenticate/admin/strategies/__tests__/auth-code-flow/auth-code-flow/auth-path.test.ts rename to packages/shopify-app-remix/src/server/authenticate/admin/strategies/__tests__/auth-code-flow/auth-path.test.ts index 3108e1f569..d3b78c6ce9 100644 --- a/packages/shopify-app-remix/src/server/authenticate/admin/strategies/__tests__/auth-code-flow/auth-code-flow/auth-path.test.ts +++ b/packages/shopify-app-remix/src/server/authenticate/admin/strategies/__tests__/auth-code-flow/auth-path.test.ts @@ -1,4 +1,4 @@ -import {shopifyApp} from '../../../../../..'; +import {shopifyApp} from '../../../../..'; import { APP_URL, TEST_SHOP, @@ -6,12 +6,14 @@ import { expectExitIframeRedirect, getThrownResponse, testConfig, -} from '../../../../../../__test-helpers'; +} from '../../../../../__test-helpers'; describe('authorize.admin auth path', () => { test('throws an 400 Response if the shop param is missing', async () => { // GIVEN - const config = testConfig(); + const config = testConfig({ + future: {unstable_newEmbeddedAuthStrategy: false}, + }); const shopify = shopifyApp(config); // WHEN @@ -27,7 +29,9 @@ describe('authorize.admin auth path', () => { test('throws an 400 Response if the shop param is invalid', async () => { // GIVEN - const config = testConfig(); + const config = testConfig({ + future: {unstable_newEmbeddedAuthStrategy: false}, + }); const shopify = shopifyApp(config); // WHEN @@ -43,7 +47,9 @@ describe('authorize.admin auth path', () => { test('throws an 302 Response to begin auth', async () => { // GIVEN - const config = testConfig(); + const config = testConfig({ + future: {unstable_newEmbeddedAuthStrategy: false}, + }); const shopify = shopifyApp(config); // WHEN @@ -59,7 +65,9 @@ describe('authorize.admin auth path', () => { test('redirects to exit-iframe when loading the auth path while in an iframe request', async () => { // GIVEN - const config = testConfig(); + const config = testConfig({ + future: {unstable_newEmbeddedAuthStrategy: false}, + }); const shopify = shopifyApp(config); // WHEN diff --git a/packages/shopify-app-remix/src/server/authenticate/admin/strategies/__tests__/auth-code-flow/auth-code-flow/authenticate.test.ts b/packages/shopify-app-remix/src/server/authenticate/admin/strategies/__tests__/auth-code-flow/authenticate.test.ts similarity index 82% rename from packages/shopify-app-remix/src/server/authenticate/admin/strategies/__tests__/auth-code-flow/auth-code-flow/authenticate.test.ts rename to packages/shopify-app-remix/src/server/authenticate/admin/strategies/__tests__/auth-code-flow/authenticate.test.ts index 5777a663bb..93ac46f027 100644 --- a/packages/shopify-app-remix/src/server/authenticate/admin/strategies/__tests__/auth-code-flow/auth-code-flow/authenticate.test.ts +++ b/packages/shopify-app-remix/src/server/authenticate/admin/strategies/__tests__/auth-code-flow/authenticate.test.ts @@ -1,6 +1,6 @@ import {SESSION_COOKIE_NAME, Session} from '@shopify/shopify-api'; -import {shopifyApp} from '../../../../../..'; +import {shopifyApp} from '../../../../..'; import { APP_URL, BASE64_HOST, @@ -12,13 +12,19 @@ import { setUpValidSession, testConfig, signRequestCookie, -} from '../../../../../../__test-helpers'; +} from '../../../../../__test-helpers'; describe('authenticate', () => { describe('errors', () => { it('redirects to exit-iframe if app is embedded and the session is no longer valid for the id_token when embedded', async () => { // GIVEN - const shopify = shopifyApp(testConfig({scopes: ['otherTestScope']})); + const shopify = shopifyApp( + testConfig({ + scopes: ['otherTestScope'], + future: {unstable_newEmbeddedAuthStrategy: false}, + isEmbeddedApp: true, + }), + ); await setUpValidSession(shopify.sessionStorage); // WHEN @@ -37,8 +43,10 @@ describe('authenticate', () => { // manageAccessToken or ensureInstalledOnShop it('redirects to auth if there is no session cookie for non-embedded apps when at the top level', async () => { // GIVEN - const config = testConfig(); - const shopify = shopifyApp(testConfig({isEmbeddedApp: false})); + const config = testConfig({ + isEmbeddedApp: false, + }); + const shopify = shopifyApp(config); await setUpValidSession(shopify.sessionStorage); // WHEN @@ -58,8 +66,8 @@ describe('authenticate', () => { it('redirects to auth if the session is no longer valid for non-embedded apps when at the top level', async () => { // GIVEN const config = testConfig({ - isEmbeddedApp: false, scopes: ['otherTestScope'], + isEmbeddedApp: false, }); const shopify = shopifyApp(config); const session = await setUpValidSession(shopify.sessionStorage); @@ -89,15 +97,20 @@ describe('authenticate', () => { (isOnline) => { it('returns the context if the session is valid and the app is embedded', async () => { // GIVEN - const shopify = shopifyApp(testConfig({useOnlineTokens: isOnline})); + const shopify = shopifyApp( + testConfig({ + useOnlineTokens: isOnline, + future: {unstable_newEmbeddedAuthStrategy: false}, + isEmbeddedApp: true, + }), + ); let testSession: Session; testSession = await setUpValidSession(shopify.sessionStorage); if (isOnline) { - testSession = await setUpValidSession( - shopify.sessionStorage, + testSession = await setUpValidSession(shopify.sessionStorage, { isOnline, - ); + }); } // WHEN @@ -115,15 +128,18 @@ describe('authenticate', () => { it('returns the context if the session is valid and the app is not embedded', async () => { // GIVEN - const shopify = shopifyApp(testConfig({isEmbeddedApp: false})); + const shopify = shopifyApp( + testConfig({ + isEmbeddedApp: false, + }), + ); let testSession: Session; testSession = await setUpValidSession(shopify.sessionStorage); if (isOnline) { - testSession = await setUpValidSession( - shopify.sessionStorage, + testSession = await setUpValidSession(shopify.sessionStorage, { isOnline, - ); + }); } // WHEN diff --git a/packages/shopify-app-remix/src/server/authenticate/admin/strategies/__tests__/auth-code-flow/auth-code-flow/ensure-installed-on-shop.test.ts b/packages/shopify-app-remix/src/server/authenticate/admin/strategies/__tests__/auth-code-flow/ensure-installed-on-shop.test.ts similarity index 81% rename from packages/shopify-app-remix/src/server/authenticate/admin/strategies/__tests__/auth-code-flow/auth-code-flow/ensure-installed-on-shop.test.ts rename to packages/shopify-app-remix/src/server/authenticate/admin/strategies/__tests__/auth-code-flow/ensure-installed-on-shop.test.ts index 22b7e53130..7f33a0182d 100644 --- a/packages/shopify-app-remix/src/server/authenticate/admin/strategies/__tests__/auth-code-flow/auth-code-flow/ensure-installed-on-shop.test.ts +++ b/packages/shopify-app-remix/src/server/authenticate/admin/strategies/__tests__/auth-code-flow/ensure-installed-on-shop.test.ts @@ -1,6 +1,6 @@ import {LogSeverity, SESSION_COOKIE_NAME} from '@shopify/shopify-api'; -import {shopifyApp} from '../../../../../..'; +import {shopifyApp} from '../../../../..'; import { API_KEY, APP_URL, @@ -13,16 +13,16 @@ import { getJwt, getThrownResponse, setUpValidSession, - testConfig, signRequestCookie, mockExternalRequest, -} from '../../../../../../__test-helpers'; + testConfig, +} from '../../../../../__test-helpers'; describe('authorize.admin doc request path', () => { describe('errors', () => { it('redirects to auth when not embedded and there is no offline session', async () => { // GIVEN - const config = testConfig(); + const config = testConfig({isEmbeddedApp: false}); const shopify = shopifyApp(config); // WHEN @@ -37,7 +37,12 @@ describe('authorize.admin doc request path', () => { it('redirects to exit-iframe when embedded and there is no offline session', async () => { // GIVEN - const shopify = shopifyApp(testConfig()); + const shopify = shopifyApp( + testConfig({ + future: {unstable_newEmbeddedAuthStrategy: false}, + isEmbeddedApp: true, + }), + ); // WHEN const response = await getThrownResponse( @@ -53,7 +58,10 @@ describe('authorize.admin doc request path', () => { it('redirects to auth when not embedded on an embedded app, and the API token is invalid', async () => { // GIVEN - const config = testConfig(); + const config = testConfig({ + future: {unstable_newEmbeddedAuthStrategy: false}, + isEmbeddedApp: true, + }); const shopify = shopifyApp(config); await setUpValidSession(shopify.sessionStorage); @@ -74,7 +82,10 @@ describe('authorize.admin doc request path', () => { it('returns non-401 codes when not embedded on an embedded app and the request fails', async () => { // GIVEN - const config = testConfig(); + const config = testConfig({ + future: {unstable_newEmbeddedAuthStrategy: false}, + isEmbeddedApp: true, + }); const shopify = shopifyApp(config); await setUpValidSession(shopify.sessionStorage); @@ -102,7 +113,10 @@ describe('authorize.admin doc request path', () => { it('returns a 500 when not embedded on an embedded app and the request fails', async () => { // GIVEN - const config = testConfig(); + const config = testConfig({ + future: {unstable_newEmbeddedAuthStrategy: false}, + isEmbeddedApp: true, + }); const shopify = shopifyApp(config); await setUpValidSession(shopify.sessionStorage); @@ -127,34 +141,14 @@ describe('authorize.admin doc request path', () => { ); }); - it("redirects to the embedded app URL if there is a valid session but the app isn't embedded yet", async () => { - // GIVEN - const config = testConfig(); - const shopify = shopifyApp(testConfig()); - await setUpValidSession(shopify.sessionStorage); - - await mockExternalRequest({ - request: new Request(GRAPHQL_URL, {method: 'POST'}), - response: new Response(undefined, {status: 200}), - }); - - // WHEN - const response = await getThrownResponse( - shopify.authenticate.admin, - new Request(`${APP_URL}?shop=${TEST_SHOP}&host=${BASE64_HOST}`), - ); - - // THEN - const {hostname, pathname} = new URL(response.headers.get('location')!); - - expect(response.status).toBe(302); - expect(hostname).toBe(SHOPIFY_HOST); - expect(pathname).toBe(`/apps/${API_KEY}`); - }); - it('redirects to exit-iframe if app is embedded and there is no session for the id_token when embedded', async () => { // GIVEN - const shopify = shopifyApp(testConfig()); + const shopify = shopifyApp( + testConfig({ + future: {unstable_newEmbeddedAuthStrategy: false}, + isEmbeddedApp: true, + }), + ); await setUpValidSession(shopify.sessionStorage); const otherShopDomain = 'other-shop.myshopify.io'; @@ -171,11 +165,12 @@ describe('authorize.admin doc request path', () => { expectExitIframeRedirect(response, {shop: otherShopDomain}); }); - // manageAccessToken or ensureInstalledOnShop it('redirects to auth if there is no session cookie for non-embedded apps when at the top level', async () => { // GIVEN - const config = testConfig(); - const shopify = shopifyApp(testConfig({isEmbeddedApp: false})); + const config = testConfig({ + isEmbeddedApp: false, + }); + const shopify = shopifyApp(config); await setUpValidSession(shopify.sessionStorage); // WHEN @@ -218,7 +213,6 @@ describe('authorize.admin doc request path', () => { }); }); - // manageAccessToken & ensureInstalledOnShop it('loads a session from the cookie from a request with no search params when not embedded', async () => { // GIVEN const shopify = shopifyApp(testConfig({isEmbeddedApp: false})); diff --git a/packages/shopify-app-remix/src/server/authenticate/admin/strategies/__tests__/auth-code-flow/session-token-header-path.test.ts b/packages/shopify-app-remix/src/server/authenticate/admin/strategies/__tests__/auth-code-flow/session-token-header-path.test.ts new file mode 100644 index 0000000000..56d14ae7ea --- /dev/null +++ b/packages/shopify-app-remix/src/server/authenticate/admin/strategies/__tests__/auth-code-flow/session-token-header-path.test.ts @@ -0,0 +1,79 @@ +import {SESSION_COOKIE_NAME, Session} from '@shopify/shopify-api'; + +import {shopifyApp} from '../../../../..'; +import { + APP_URL, + BASE64_HOST, + TEST_SHOP, + getJwt, + getThrownResponse, + setUpValidSession, + testConfig, +} from '../../../../../__test-helpers'; +import {REAUTH_URL_HEADER} from '../../../../const'; + +describe('authorize.session token header path', () => { + describe('errors', () => { + describe.each([true, false])('when isOnline: %s', (isOnline) => { + it(`returns app bridge redirection headers if there is no session`, async () => { + // GIVEN + const shopify = shopifyApp( + testConfig({ + useOnlineTokens: isOnline, + future: {unstable_newEmbeddedAuthStrategy: false}, + }), + ); + + // WHEN + const {token} = getJwt(); + const response = await getThrownResponse( + shopify.authenticate.admin, + new Request(`${APP_URL}?shop=${TEST_SHOP}&host=${BASE64_HOST}`, { + headers: {Authorization: `Bearer ${token}`}, + }), + ); + + // THEN + const {origin, pathname, searchParams} = new URL( + response.headers.get(REAUTH_URL_HEADER)!, + ); + + expect(response.status).toBe(401); + expect(origin).toBe(APP_URL); + expect(pathname).toBe('/auth'); + expect(searchParams.get('shop')).toBe(TEST_SHOP); + }); + + it(`returns app bridge redirection headers if the session is no longer valid`, async () => { + // GIVEN + const shopify = shopifyApp( + testConfig({ + useOnlineTokens: isOnline, + scopes: ['otherTestScope'], + future: {unstable_newEmbeddedAuthStrategy: false}, + }), + ); + // The session scopes don't match the configured scopes, so it needs to be reset + await setUpValidSession(shopify.sessionStorage, {isOnline}); + + // WHEN + const {token} = getJwt(); + const response = await getThrownResponse( + shopify.authenticate.admin, + new Request(`${APP_URL}?shop=${TEST_SHOP}&host=${BASE64_HOST}`, { + headers: {Authorization: `Bearer ${token}`}, + }), + ); + + // THEN + const {origin, pathname, searchParams} = new URL( + response.headers.get(REAUTH_URL_HEADER)!, + ); + expect(response.status).toBe(401); + expect(origin).toBe(APP_URL); + expect(pathname).toBe('/auth'); + expect(searchParams.get('shop')).toBe(TEST_SHOP); + }); + }); + }); +}); diff --git a/packages/shopify-app-remix/src/server/authenticate/admin/strategies/__tests__/token-exchange/admin-client.test.ts b/packages/shopify-app-remix/src/server/authenticate/admin/strategies/__tests__/token-exchange/admin-client.test.ts new file mode 100644 index 0000000000..d3c6f5ff49 --- /dev/null +++ b/packages/shopify-app-remix/src/server/authenticate/admin/strategies/__tests__/token-exchange/admin-client.test.ts @@ -0,0 +1,189 @@ +import { + ApiVersion, + HttpMaxRetriesError, + LATEST_API_VERSION, + SESSION_COOKIE_NAME, + Session, +} from '@shopify/shopify-api'; +import {restResources} from '@shopify/shopify-api/rest/admin/2023-04'; + +import { + APP_URL, + BASE64_HOST, + TEST_SHOP, + expectExitIframeRedirect, + getJwt, + getThrownResponse, + setUpValidSession, + signRequestCookie, + testConfig, + mockExternalRequest, + expectAdminApiClient, +} from '../../../../../__test-helpers'; +import {shopifyApp} from '../../../../..'; +import { + REAUTH_URL_HEADER, + RETRY_INVALID_SESSION_HEADER, +} from '../../../../const'; +import {AdminApiContext} from '../../../../../clients'; + +describe('admin.authenticate context', () => { + expectAdminApiClient(async () => { + const { + admin, + expectedSession, + session: actualSession, + } = await setUpDocumentFlow(); + + return {admin, expectedSession, actualSession}; + }); + describe.each([ + { + testGroup: 'REST client', + mockRequest: mockRestRequest, + action: async (admin: AdminApiContext, _session: Session) => + admin.rest.get({path: '/customers.json'}), + }, + { + testGroup: 'REST resources', + mockRequest: mockRestRequest, + action: async (admin: AdminApiContext, session: Session) => + admin.rest.resources.Customer.all({session}), + }, + { + testGroup: 'GraphQL client', + mockRequest: mockGraphqlRequest(), + action: async (admin: AdminApiContext, _session: Session) => + admin.graphql('{ shop { name } }'), + }, + { + testGroup: 'GraphQL client with options', + mockRequest: mockGraphqlRequest('2021-01' as ApiVersion), + action: async (admin: AdminApiContext, _session: Session) => + admin.graphql( + 'mutation myMutation($ID: String!) { shop(ID: $ID) { name } }', + { + variables: {ID: '123'}, + apiVersion: '2021-01' as ApiVersion, + headers: {custom: 'header'}, + tries: 2, + }, + ), + }, + ])( + '$testGroup re-authentication', + ({testGroup: _testGroup, mockRequest, action}) => { + it('redirects to exit bounce page when document request receives a 401 response', async () => { + // GIVEN + const {admin, session, shopify} = await setUpDocumentFlow(); + const requestMock = await mockRequest(); + + // WHEN + const response = await getThrownResponse( + async () => action(admin, session), + requestMock, + ); + + // THEN + expect(response.status).toBe(302); + + const {pathname} = new URL(response.headers.get('location')!, APP_URL); + expect(pathname).toBe('/auth/session-token'); + + expect( + await shopify.sessionStorage.loadSession(session.id), + ).toBeUndefined(); + }); + + it('returns 401 when receives a 401 response on fetch requests', async () => { + // GIVEN + const {admin, session, shopify} = await setUpFetchFlow(); + const requestMock = await mockRequest(); + + // WHEN + const response = await getThrownResponse( + async () => action(admin, session), + requestMock, + ); + + // THEN + expect(response.status).toEqual(401); + expect( + response.headers.get('X-Shopify-Retry-Invalid-Session-Request'), + ).toBeNull(); + + expect( + await shopify.sessionStorage.loadSession(session.id), + ).toBeUndefined(); + }); + }, + ); +}); + +async function setUpDocumentFlow() { + const shopify = shopifyApp( + testConfig({ + restResources, + }), + ); + const expectedSession = await setUpValidSession(shopify.sessionStorage); + + const {token} = getJwt(); + const request = new Request( + `${APP_URL}?embedded=1&shop=${TEST_SHOP}&host=${BASE64_HOST}&id_token=${token}`, + ); + + return { + shopify, + expectedSession, + ...(await shopify.authenticate.admin(request)), + }; +} + +async function setUpFetchFlow() { + const shopify = shopifyApp( + testConfig({ + restResources, + }), + ); + await setUpValidSession(shopify.sessionStorage); + + const {token} = getJwt(); + const request = new Request(APP_URL, { + headers: {Authorization: `Bearer ${token}`}, + }); + + return { + shopify, + ...(await shopify.authenticate.admin(request)), + }; +} + +async function mockRestRequest(status = 401) { + const requestMock = new Request( + `https://${TEST_SHOP}/admin/api/${LATEST_API_VERSION}/customers.json`, + ); + + await mockExternalRequest({ + request: requestMock, + response: new Response('{}', {status}), + }); + + return requestMock; +} + +function mockGraphqlRequest(apiVersion = LATEST_API_VERSION) { + return async function (status = 401) { + const requestMock = new Request( + `https://${TEST_SHOP}/admin/api/${apiVersion}/graphql.json`, + {method: 'POST'}, + ); + + await mockExternalRequest({ + request: requestMock, + response: new Response(undefined, {status}), + }); + + return requestMock; + }; +} diff --git a/packages/shopify-app-remix/src/server/authenticate/admin/strategies/__tests__/token-exchange/authenticate.test.ts b/packages/shopify-app-remix/src/server/authenticate/admin/strategies/__tests__/token-exchange/authenticate.test.ts new file mode 100644 index 0000000000..563288e299 --- /dev/null +++ b/packages/shopify-app-remix/src/server/authenticate/admin/strategies/__tests__/token-exchange/authenticate.test.ts @@ -0,0 +1,312 @@ +import {Session} from '@shopify/shopify-api'; + +import {shopifyApp} from '../../../../..'; +import { + API_KEY, + API_SECRET_KEY, + APP_URL, + BASE64_HOST, + TEST_SHOP, + getJwt, + getThrownResponse, + mockExternalRequest, + setUpValidSession, + testConfig, +} from '../../../../../__test-helpers'; + +const USER_ID = 902541635; + +describe('authenticate', () => { + it('performs token exchange when there is no offline session', async () => { + // GIVEN + const config = testConfig(); + const shopify = shopifyApp(config); + + const {token} = getJwt(); + await mockTokenExchangeRequest(token, 'offline'); + + // WHEN + const {session} = await shopify.authenticate.admin( + new Request( + `${APP_URL}?embedded=1&shop=${TEST_SHOP}&host=${BASE64_HOST}&id_token=${token}`, + ), + ); + + // THEN + const [persistedSession] = await config.sessionStorage.findSessionsByShop( + TEST_SHOP, + ); + + expect(persistedSession).toEqual(session); + expect(session).toMatchObject({ + accessToken: '123abc-exchanged-from-session-token', + id: `offline_${TEST_SHOP}`, + isOnline: false, + scope: 'read_orders', + shop: TEST_SHOP, + state: '', + }); + }); + + it('performs token exchange when existing session is no longer valid', async () => { + // GIVEN + const config = testConfig({useOnlineTokens: true}); + const shopify = shopifyApp(config); + const anHourAgo = new Date(Date.now() - 1000 * 3600); + await setUpValidSession(shopify.sessionStorage, { + isOnline: true, + expires: anHourAgo, + }); + + const {token} = getJwt(); + await mockTokenExchangeRequest(token, 'offline'); + await mockTokenExchangeRequest(token, 'online'); + + // WHEN + const {session} = await shopify.authenticate.admin( + new Request( + `${APP_URL}?embedded=1&shop=${TEST_SHOP}&host=${BASE64_HOST}&id_token=${token}`, + ), + ); + + // THEN + const [_, onlineSession] = await config.sessionStorage.findSessionsByShop( + TEST_SHOP, + ); + + expect(onlineSession).toEqual(session); + expect(session).toMatchObject({ + accessToken: '123abc-exchanged-from-session-token', + id: `${TEST_SHOP}_${USER_ID}`, + isOnline: true, + scope: 'read_orders', + shop: TEST_SHOP, + state: '', + onlineAccessInfo: expect.any(Object), + }); + }); + + describe.each([true, false])( + 'existing sessions when isOnline: %s', + (isOnline) => { + it('returns the context if the session is valid', async () => { + // GIVEN + const shopify = shopifyApp(testConfig({useOnlineTokens: isOnline})); + + let testSession: Session; + testSession = await setUpValidSession(shopify.sessionStorage); + if (isOnline) { + testSession = await setUpValidSession(shopify.sessionStorage, { + isOnline, + }); + } + + // WHEN + const {token} = getJwt(); + const {admin, session} = await shopify.authenticate.admin( + new Request( + `${APP_URL}?embedded=1&shop=${TEST_SHOP}&host=${BASE64_HOST}&id_token=${token}`, + ), + ); + + // THEN + expect(session).toBe(testSession); + expect(admin.rest.session).toBe(testSession); + expect(session.isOnline).toEqual(isOnline); + }); + }, + ); + + test('redirects to bounce page on document request when receiving an invalid subject token response from token exchange API', async () => { + // GIVEN + const config = testConfig(); + const shopify = shopifyApp(config); + + const {token} = getJwt(); + await mockInvalidTokenExchangeRequest('invalid_subject_token'); + + // WHEN + const response = await getThrownResponse( + shopify.authenticate.admin, + new Request( + `${APP_URL}?embedded=1&shop=${TEST_SHOP}&host=${BASE64_HOST}&id_token=${token}`, + ), + ); + + // THEN + const {pathname, searchParams} = new URL( + response.headers.get('location')!, + APP_URL, + ); + + expect(response.status).toBe(302); + expect(pathname).toBe('/auth/session-token'); + expect(searchParams.get('shop')).toBe(TEST_SHOP); + expect(searchParams.get('host')).toBe(BASE64_HOST); + expect(searchParams.get('shopify-reload')).toBe( + `${APP_URL}/?embedded=1&shop=${TEST_SHOP}&host=${BASE64_HOST}`, + ); + }); + + test('throws 401 unauthorized on XHR request when receiving an invalid subject token response from token exchange API', async () => { + // GIVEN + const config = testConfig(); + const shopify = shopifyApp(config); + + const {token} = getJwt(); + await mockInvalidTokenExchangeRequest('invalid_subject_token'); + + // WHEN + const response = await getThrownResponse( + shopify.authenticate.admin, + new Request(APP_URL, { + headers: { + Authorization: `Bearer ${token}`, + }, + }), + ); + + // THEN + expect(response.status).toBe(401); + expect( + response.headers.get('X-Shopify-Retry-Invalid-Session-Request'), + ).toEqual('1'); + }); + + test('throws 500 for any other error from token exchange API', async () => { + // GIVEN + const config = testConfig(); + const shopify = shopifyApp(config); + + const {token} = getJwt(); + await mockInvalidTokenExchangeRequest('im_broke', 401); + + // WHEN + const response = await getThrownResponse( + shopify.authenticate.admin, + new Request(APP_URL, { + headers: { + Authorization: `Bearer ${token}`, + }, + }), + ); + + // THEN + expect(response.status).toBe(500); + }); + + test('throws a 500 if afterAuth hook throws an error', async () => { + // GIVEN + const config = testConfig(); + const shopify = shopifyApp({ + ...config, + hooks: { + afterAuth: () => { + throw new Error('test'); + }, + }, + }); + + const {token} = getJwt(); + await mockTokenExchangeRequest(token, 'offline'); + + // WHEN + const response = await getThrownResponse( + shopify.authenticate.admin, + new Request( + `${APP_URL}?embedded=1&shop=${TEST_SHOP}&host=${BASE64_HOST}&id_token=${token}`, + ), + ); + + // THEN + expect(response.status).toBe(500); + }); + + test('Runs the afterAuth hooks passing', async () => { + // GIVEN + const afterAuthMock = jest.fn(); + const config = testConfig({ + hooks: { + afterAuth: afterAuthMock, + }, + }); + const shopify = shopifyApp(config); + + const {token} = getJwt(); + await mockTokenExchangeRequest(token, 'offline'); + + // WHEN + const {session} = await shopify.authenticate.admin( + new Request( + `${APP_URL}?embedded=1&shop=${TEST_SHOP}&host=${BASE64_HOST}&id_token=${token}`, + ), + ); + + // THEN + expect(session).toBeDefined(); + expect(afterAuthMock).toHaveBeenCalledTimes(1); + }); +}); + +async function mockTokenExchangeRequest( + sessionToken, + tokenType: 'online' | 'offline' = 'offline', +) { + const responseBody = { + access_token: '123abc-exchanged-from-session-token', + scope: 'read_orders', + }; + + await mockExternalRequest({ + request: new Request(`https://${TEST_SHOP}/admin/oauth/access_token`, { + method: 'POST', + body: JSON.stringify({ + client_id: API_KEY, + client_secret: API_SECRET_KEY, + grant_type: 'urn:ietf:params:oauth:grant-type:token-exchange', + subject_token: sessionToken, + subject_token_type: 'urn:ietf:params:oauth:token-type:id_token', + requested_token_type: + tokenType === 'offline' + ? 'urn:shopify:params:oauth:token-type:offline-access-token' + : 'urn:shopify:params:oauth:token-type:online-access-token', + }), + }), + response: + tokenType === 'offline' + ? new Response(JSON.stringify(responseBody)) + : new Response( + JSON.stringify({ + ...responseBody, + expires_in: Math.trunc(Date.now() / 1000) + 3600, + associated_user_scope: 'read_orders', + associated_user: { + id: USER_ID, + first_name: 'John', + last_name: 'Smith', + email: 'john@example.com', + email_verified: true, + account_owner: true, + locale: 'en', + collaborator: false, + }, + }), + ), + }); +} + +async function mockInvalidTokenExchangeRequest(error: string, status = 400) { + await mockExternalRequest({ + request: new Request(`https://${TEST_SHOP}/admin/oauth/access_token`, { + method: 'POST', + }), + response: new Response( + JSON.stringify({ + error, + }), + { + status, + }, + ), + }); +} diff --git a/packages/shopify-app-remix/src/server/authenticate/admin/strategies/auth-code-flow.ts b/packages/shopify-app-remix/src/server/authenticate/admin/strategies/auth-code-flow.ts index 20b87cbad1..bd4b786ab0 100644 --- a/packages/shopify-app-remix/src/server/authenticate/admin/strategies/auth-code-flow.ts +++ b/packages/shopify-app-remix/src/server/authenticate/admin/strategies/auth-code-flow.ts @@ -12,6 +12,7 @@ import { import type {BasicParams} from '../../../types'; import { beginAuth, + handleClientErrorFactory, redirectToAuthPage, redirectToShopifyOrAppRoot, redirectWithExitIframe, @@ -20,8 +21,9 @@ import { } from '../helpers'; import {AppConfig} from '../../../config-types'; import {getSessionTokenHeader} from '../../helpers'; +import {HandleAdminClientError} from '../../../clients'; -import {AuthorizationStrategy} from './types'; +import {AuthorizationStrategy, SessionContext, OnErrorOptions} from './types'; export class AuthCodeFlowStrategy< Resources extends ShopifyRestResources = ShopifyRestResources, @@ -65,11 +67,12 @@ export class AuthCodeFlowStrategy< public async authenticate( request: Request, - session: Session | undefined, - shop: string, + sessionContext: SessionContext, ): Promise { const {api, config, logger} = this; + const {shop, session} = sessionContext; + if (!session) { logger.debug('No session found, redirecting to OAuth', {shop}); await redirectToAuthPage({config, logger, api}, request, shop); @@ -86,6 +89,22 @@ export class AuthCodeFlowStrategy< return session!; } + public handleClientError(request: Request): HandleAdminClientError { + const {api, config, logger} = this; + return handleClientErrorFactory({ + request, + onError: async ({session, error}: OnErrorOptions) => { + if (error.response.code === 401) { + throw await redirectToAuthPage( + {api, config, logger}, + request, + session.shop, + ); + } + }, + }); + } + private async ensureInstalledOnShop(request: Request) { const {api, config, logger} = this; @@ -179,6 +198,7 @@ export class AuthCodeFlowStrategy< {api, config, logger}, session, request, + this, ); throw await redirectToShopifyOrAppRoot( @@ -286,7 +306,7 @@ export class AuthCodeFlowStrategy< } else if (error instanceof GraphqlQueryError) { const context: {[key: string]: string} = {shop}; if (error.response) { - context.response = JSON.stringify(error.response); + context.response = JSON.stringify(error.body); } logger.error( diff --git a/packages/shopify-app-remix/src/server/authenticate/admin/strategies/token-exchange.ts b/packages/shopify-app-remix/src/server/authenticate/admin/strategies/token-exchange.ts new file mode 100644 index 0000000000..f1c0308ae0 --- /dev/null +++ b/packages/shopify-app-remix/src/server/authenticate/admin/strategies/token-exchange.ts @@ -0,0 +1,169 @@ +import { + HttpResponseError, + InvalidJwtError, + RequestedTokenType, + Session, + Shopify, + ShopifyRestResources, +} from '@shopify/shopify-api'; + +import {AppConfig, AppConfigArg} from '../../../config-types'; +import { + BasicParams, + ApiConfigWithFutureFlags, + ApiFutureFlags, +} from '../../../types'; +import {respondToInvalidSessionToken} from '../../helpers'; +import {handleClientErrorFactory, triggerAfterAuthHook} from '../helpers'; +import {HandleAdminClientError} from '../../../clients'; + +import {AuthorizationStrategy, SessionContext, OnErrorOptions} from './types'; + +export class TokenExchangeStrategy + implements AuthorizationStrategy +{ + protected api: Shopify< + ApiConfigWithFutureFlags, + ShopifyRestResources, + ApiFutureFlags + >; + + protected config: AppConfig; + protected logger: Shopify['logger']; + + public constructor({api, config, logger}: BasicParams) { + this.api = api; + this.config = config; + this.logger = logger; + } + + public async respondToOAuthRequests(_request: Request): Promise {} + + public async authenticate( + request: Request, + sessionContext: SessionContext, + ): Promise { + const {api, config, logger} = this; + const {shop, session, sessionToken} = sessionContext; + + if (!sessionToken) throw new InvalidJwtError(); + + if (!session || session.isExpired()) { + logger.info('No valid session found'); + logger.info('Requesting offline access token'); + const {session: offlineSession} = await this.exchangeToken({ + request, + sessionToken, + shop, + requestedTokenType: RequestedTokenType.OfflineAccessToken, + }); + + await config.sessionStorage.storeSession(offlineSession); + + let newSession = offlineSession; + + if (config.useOnlineTokens) { + logger.info('Requesting online access token'); + const {session: onlineSession} = await this.exchangeToken({ + request, + sessionToken, + shop, + requestedTokenType: RequestedTokenType.OnlineAccessToken, + }); + + await config.sessionStorage.storeSession(onlineSession); + newSession = onlineSession; + } + + try { + await this.handleAfterAuthHook( + {api, config, logger}, + newSession, + request, + sessionToken, + ); + } catch (error) { + throw new Response(undefined, { + status: 500, + statusText: 'Internal Server Error', + }); + } + + return newSession; + } + + return session!; + } + + public handleClientError(request: Request): HandleAdminClientError { + const {api, config, logger} = this; + return handleClientErrorFactory({ + request, + onError: async ({session, error}: OnErrorOptions) => { + if (error.response.code === 401) { + config.sessionStorage.deleteSession(session.id); + + respondToInvalidSessionToken({ + params: {config, api, logger}, + request, + }); + } + }, + }); + } + + private async exchangeToken({ + request, + shop, + sessionToken, + requestedTokenType, + }: { + request: Request; + shop: string; + sessionToken: string; + requestedTokenType: RequestedTokenType; + }): Promise<{session: Session}> { + const {api, config, logger} = this; + + try { + return await api.auth.tokenExchange({ + sessionToken, + shop, + requestedTokenType, + }); + } catch (error) { + if ( + error instanceof InvalidJwtError || + (error instanceof HttpResponseError && + error.response.code === 400 && + error.response.body?.error === 'invalid_subject_token') + ) { + throw respondToInvalidSessionToken({ + params: {api, config, logger}, + request, + retryRequest: true, + }); + } + + throw new Response(undefined, { + status: 500, + statusText: 'Internal Server Error', + }); + } + } + + private async handleAfterAuthHook( + params: BasicParams, + session: Session, + request: Request, + sessionToken: string, + ) { + const {config} = params; + await config.idempotentPromiseHandler.handlePromise({ + promiseFunction: () => { + return triggerAfterAuthHook(params, session, request, this); + }, + identifier: sessionToken, + }); + } +} diff --git a/packages/shopify-app-remix/src/server/authenticate/admin/strategies/types.ts b/packages/shopify-app-remix/src/server/authenticate/admin/strategies/types.ts index aa472df4c0..f852a9d185 100644 --- a/packages/shopify-app-remix/src/server/authenticate/admin/strategies/types.ts +++ b/packages/shopify-app-remix/src/server/authenticate/admin/strategies/types.ts @@ -1,16 +1,29 @@ -import {JwtPayload, Session} from '@shopify/shopify-api'; +import {HttpResponseError, Session} from '@shopify/shopify-api'; -export interface SessionTokenContext { +import {HandleAdminClientError} from '../../../clients'; + +export interface SessionContext { shop: string; - sessionId?: string; - payload?: JwtPayload; + session?: Session; + sessionToken?: string; +} + +export interface OnErrorOptions { + request: Request; + session: Session; + error: HttpResponseError; +} + +export interface HandleClientErrorOptions { + request: Request; + onError?: ({session, error}: OnErrorOptions) => Promise; } export interface AuthorizationStrategy { respondToOAuthRequests: (request: Request) => Promise; authenticate: ( request: Request, - session: Session | undefined, - shop: string, + sessionContext: SessionContext, ) => Promise; + handleClientError: (request: Request) => HandleAdminClientError; } 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 3000e937ef..96dedc4885 100644 --- a/packages/shopify-app-remix/src/server/authenticate/admin/types.ts +++ b/packages/shopify-app-remix/src/server/authenticate/admin/types.ts @@ -197,8 +197,3 @@ export type AuthenticateAdmin< Config extends AppConfigArg, Resources extends ShopifyRestResources = ShopifyRestResources, > = (request: Request) => Promise>; - -export interface SessionContext { - session: Session; - token?: JwtPayload; -} diff --git a/packages/shopify-app-remix/src/server/authenticate/const.ts b/packages/shopify-app-remix/src/server/authenticate/const.ts index 4aa2a4a218..481f931385 100644 --- a/packages/shopify-app-remix/src/server/authenticate/const.ts +++ b/packages/shopify-app-remix/src/server/authenticate/const.ts @@ -4,6 +4,10 @@ export const APP_BRIDGE_URL = export const REAUTH_URL_HEADER = 'X-Shopify-API-Request-Failure-Reauthorize-Url'; +export const RETRY_INVALID_SESSION_HEADER = { + 'X-Shopify-Retry-Invalid-Session-Request': '1', +}; + export const CORS_HEADERS = { 'Access-Control-Allow-Origin': '*', 'Access-Control-Allow-Headers': 'Authorization', diff --git a/packages/shopify-app-remix/src/server/authenticate/helpers/__tests__/idempotent-promise-handler.test.ts b/packages/shopify-app-remix/src/server/authenticate/helpers/__tests__/idempotent-promise-handler.test.ts new file mode 100644 index 0000000000..235ab2d986 --- /dev/null +++ b/packages/shopify-app-remix/src/server/authenticate/helpers/__tests__/idempotent-promise-handler.test.ts @@ -0,0 +1,104 @@ +import {ShopifyError} from '@shopify/shopify-api'; + +import {IdempotentPromiseHandler} from '../idempotent-promise-handler'; + +const mockFunction = jest.fn(); +async function promiseFunction() { + mockFunction(); +} + +afterEach(() => { + mockFunction.mockReset(); +}); + +describe('IdempotentPromiseHandler', () => { + it('runs the promise function only once for the same identifier', async () => { + // GIVEN + const promiseHandler = new IdempotentPromiseHandler(); + + // WHEN + promiseHandler.handlePromise({ + promiseFunction, + identifier: 'first-promise', + }); + + promiseHandler.handlePromise({ + promiseFunction, + identifier: 'first-promise', + }); + + // THEN + expect(mockFunction).toHaveBeenCalledTimes(1); + }); + + it('runs the promise function once for each identifier', async () => { + // GIVEN + // const promiseFunction = jest.fn(); + const promiseHandler = new IdempotentPromiseHandler(); + + // WHEN + promiseHandler.handlePromise({ + promiseFunction, + identifier: 'first-promise', + }); + + promiseHandler.handlePromise({ + promiseFunction, + identifier: 'second-promise', + }); + + // THEN + expect(mockFunction).toHaveBeenCalledTimes(2); + }); + + it('clears stale identifier from hash', async () => { + // GIVEN + const currentDate = Date.now(); + jest.useFakeTimers().setSystemTime(currentDate); + const promiseHandler = new IdempotentPromiseHandler() as any; + + // WHEN + promiseHandler.handlePromise({ + promiseFunction, + identifier: 'old-promise', + }); + + jest.useFakeTimers().setSystemTime(currentDate + 70000); + + await promiseHandler.handlePromise({ + promiseFunction, + identifier: 'new-promise', + }); + + expect(promiseHandler.identifiers.size).toBe(1); + }); + + it('clears stale identifier from hash even when promise fails', async () => { + // GIVEN + const promiseFunctionErr = () => { + throw new ShopifyError(); + }; + const currentDate = Date.now(); + jest.useFakeTimers().setSystemTime(currentDate); + const promiseHandler = new IdempotentPromiseHandler() as any; + + // WHEN + expect( + promiseHandler.handlePromise({ + promiseFunction: promiseFunctionErr, + identifier: 'old-promise', + }), + ).rejects.toThrow(); + + jest.useFakeTimers().setSystemTime(currentDate + 70000); + + expect( + promiseHandler.handlePromise({ + promiseFunction: promiseFunctionErr, + identifier: 'new-promise', + }), + ).rejects.toThrow(); + + expect(promiseHandler.identifiers.size).toBe(1); + }); +}); diff --git a/packages/shopify-app-remix/src/server/authenticate/helpers/idempotent-promise-handler.ts b/packages/shopify-app-remix/src/server/authenticate/helpers/idempotent-promise-handler.ts new file mode 100644 index 0000000000..e944546ad0 --- /dev/null +++ b/packages/shopify-app-remix/src/server/authenticate/helpers/idempotent-promise-handler.ts @@ -0,0 +1,45 @@ +export interface IdempotentHandlePromiseParams { + promiseFunction: () => Promise; + identifier: string; +} + +const IDENTIFIER_TTL_MS = 60000; + +export class IdempotentPromiseHandler { + protected identifiers: Map; + + constructor() { + this.identifiers = new Map(); + } + + async handlePromise({ + promiseFunction, + identifier, + }: IdempotentHandlePromiseParams): Promise { + try { + if (this.isPromiseRunnable(identifier)) { + await promiseFunction(); + } + } finally { + this.clearStaleIdentifiers(); + } + + return Promise.resolve(); + } + + private isPromiseRunnable(identifier: string) { + if (!this.identifiers.has(identifier)) { + this.identifiers.set(identifier, Date.now()); + return true; + } + return false; + } + + private async clearStaleIdentifiers() { + this.identifiers.forEach((date, identifier, map) => { + if (Date.now() - date > IDENTIFIER_TTL_MS) { + map.delete(identifier); + } + }); + } +} diff --git a/packages/shopify-app-remix/src/server/authenticate/helpers/index.ts b/packages/shopify-app-remix/src/server/authenticate/helpers/index.ts index 351221a750..383a37bd9a 100644 --- a/packages/shopify-app-remix/src/server/authenticate/helpers/index.ts +++ b/packages/shopify-app-remix/src/server/authenticate/helpers/index.ts @@ -5,3 +5,4 @@ export * from './validate-session-token'; export * from './get-session-token-header'; export * from './reject-bot-request'; export * from './respond-to-options-request'; +export * from './respond-to-invalid-session-token'; diff --git a/packages/shopify-app-remix/src/server/authenticate/helpers/respond-to-invalid-session-token.ts b/packages/shopify-app-remix/src/server/authenticate/helpers/respond-to-invalid-session-token.ts new file mode 100644 index 0000000000..8b215d781a --- /dev/null +++ b/packages/shopify-app-remix/src/server/authenticate/helpers/respond-to-invalid-session-token.ts @@ -0,0 +1,29 @@ +import {BasicParams} from 'src/server/types'; + +import {redirectToBouncePage} from '../admin/helpers/redirect-to-bounce-page'; +import {RETRY_INVALID_SESSION_HEADER} from '../const'; + +interface RespondToInvalidSessionTokenParams { + params: BasicParams; + request: Request; + retryRequest?: boolean; +} + +export function respondToInvalidSessionToken({ + params, + request, + retryRequest = false, +}: RespondToInvalidSessionTokenParams) { + const {api, logger, config} = params; + + const isDocumentRequest = !request.headers.get('authorization'); + if (isDocumentRequest) { + return redirectToBouncePage({api, logger, config}, new URL(request.url)); + } + + throw new Response(undefined, { + status: 401, + statusText: 'Unauthorized', + headers: retryRequest ? RETRY_INVALID_SESSION_HEADER : {}, + }); +} diff --git a/packages/shopify-app-remix/src/server/authenticate/login/__tests__/login.test.ts b/packages/shopify-app-remix/src/server/authenticate/login/__tests__/login.test.ts index ce957516ef..f7bd8de25b 100644 --- a/packages/shopify-app-remix/src/server/authenticate/login/__tests__/login.test.ts +++ b/packages/shopify-app-remix/src/server/authenticate/login/__tests__/login.test.ts @@ -2,6 +2,7 @@ import {LoginErrorType, shopifyApp} from '../../../index'; import { APP_URL, TEST_SHOP, + TEST_SHOP_NAME, getThrownResponse, testConfig, } from '../../../__test-helpers'; @@ -79,25 +80,63 @@ describe('login helper', () => { }, ); - it.each([ - {urlShop: null, formShop: TEST_SHOP, method: 'POST'}, - {urlShop: TEST_SHOP, formShop: null, method: 'GET'}, - {urlShop: null, formShop: 'test-shop', method: 'POST'}, - {urlShop: 'test-shop', formShop: null, method: 'GET'}, - {urlShop: null, formShop: 'test-shop.myshopify.com', method: 'POST'}, - {urlShop: 'test-shop.myshopify.com', formShop: null, method: 'GET'}, - ])( - 'returns a redirect to /auth if the shop is valid: %s', - async ({urlShop, formShop, method}) => { + describe.each([ + {isEmbeddedApp: false, futureFlag: false, redirectToInstall: false}, + {isEmbeddedApp: true, futureFlag: false, redirectToInstall: false}, + {isEmbeddedApp: false, futureFlag: true, redirectToInstall: false}, + {isEmbeddedApp: true, futureFlag: true, redirectToInstall: true}, + ])('Given setup: %s', (testCaseConfig) => { + it.each([ + {urlShop: null, formShop: TEST_SHOP, method: 'POST'}, + {urlShop: TEST_SHOP, formShop: null, method: 'GET'}, + {urlShop: null, formShop: 'test-shop', method: 'POST'}, + {urlShop: 'test-shop', formShop: null, method: 'GET'}, + {urlShop: null, formShop: 'test-shop.myshopify.com', method: 'POST'}, + {urlShop: 'test-shop.myshopify.com', formShop: null, method: 'GET'}, + ])( + 'returns a redirect to auth or install if the shop is valid: %s', + async ({urlShop, formShop, method}) => { + // GIVEN + const config = testConfig({ + future: {unstable_newEmbeddedAuthStrategy: testCaseConfig.futureFlag}, + isEmbeddedApp: testCaseConfig.isEmbeddedApp, + }); + const shopify = shopifyApp(config); + const requestMock = { + url: urlShop + ? `${APP_URL}/auth/login?shop=${urlShop}` + : `${APP_URL}/auth/login`, + formData: async () => ({get: () => formShop}), + method, + }; + + // WHEN + const response = await getThrownResponse( + shopify.login, + requestMock as any as Request, + ); + + // THEN + const expectedPath = testCaseConfig.redirectToInstall + ? `https://admin.shopify.com/store/${TEST_SHOP_NAME}/oauth/install?client_id=${config.apiKey}` + : `${APP_URL}/auth?shop=${TEST_SHOP}`; + + expect(response.status).toEqual(302); + expect(response.headers.get('location')).toEqual(expectedPath); + }, + ); + + it('sanitizes the shop parameter', async () => { // GIVEN - const config = testConfig(); - const shopify = shopifyApp(testConfig()); + const config = testConfig({ + future: {unstable_newEmbeddedAuthStrategy: testCaseConfig.futureFlag}, + isEmbeddedApp: testCaseConfig.isEmbeddedApp, + }); + const shopify = shopifyApp(config); const requestMock = { - url: urlShop - ? `${APP_URL}/auth/login?shop=${urlShop}` - : `${APP_URL}/auth/login`, - formData: async () => ({get: () => formShop}), - method, + url: `${APP_URL}/auth/login`, + formData: async () => ({get: () => `https://${TEST_SHOP}/`}), + method: 'POST', }; // WHEN @@ -106,33 +145,13 @@ describe('login helper', () => { requestMock as any as Request, ); + const expectedPath = testCaseConfig.redirectToInstall + ? `https://admin.shopify.com/store/${TEST_SHOP_NAME}/oauth/install?client_id=${config.apiKey}` + : `${APP_URL}/auth?shop=${TEST_SHOP}`; + // THEN expect(response.status).toEqual(302); - expect(response.headers.get('location')).toEqual( - `${APP_URL}/auth?shop=${TEST_SHOP}`, - ); - }, - ); - - it('sanitizes the shop parameter', async () => { - // GIVEN - const shopify = shopifyApp(testConfig()); - const requestMock = { - url: `${APP_URL}/auth/login`, - formData: async () => ({get: () => `https://${TEST_SHOP}/`}), - method: 'POST', - }; - - // WHEN - const response = await getThrownResponse( - shopify.login, - requestMock as any as Request, - ); - - // THEN - expect(response.status).toEqual(302); - expect(response.headers.get('location')).toEqual( - `${APP_URL}/auth?shop=${TEST_SHOP}`, - ); + expect(response.headers.get('location')).toEqual(expectedPath); + }); }); }); diff --git a/packages/shopify-app-remix/src/server/authenticate/login/login.ts b/packages/shopify-app-remix/src/server/authenticate/login/login.ts index 900600a89f..50fb88e059 100644 --- a/packages/shopify-app-remix/src/server/authenticate/login/login.ts +++ b/packages/shopify-app-remix/src/server/authenticate/login/login.ts @@ -35,7 +35,14 @@ export function loginFactory(params: BasicParams) { return {shop: LoginErrorType.InvalidShop}; } - const redirectUrl = `${config.appUrl}${config.auth.path}?shop=${sanitizedShop}`; + const authPath = `${config.appUrl}${config.auth.path}?shop=${sanitizedShop}`; + + const adminPath = api.utils.legacyUrlToShopAdminUrl(sanitizedShop); + const installPath = `https://${adminPath}/oauth/install?client_id=${config.apiKey}`; + + const shouldInstall = + config.isEmbeddedApp && config.future.unstable_newEmbeddedAuthStrategy; + const redirectUrl = shouldInstall ? installPath : authPath; logger.info(`Redirecting login request to ${redirectUrl}`, { shop: sanitizedShop, diff --git a/packages/shopify-app-remix/src/server/authenticate/public/appProxy/__tests__/authenticate.test.ts b/packages/shopify-app-remix/src/server/authenticate/public/appProxy/__tests__/authenticate.test.ts index 81eb4bc146..9d9ff62644 100644 --- a/packages/shopify-app-remix/src/server/authenticate/public/appProxy/__tests__/authenticate.test.ts +++ b/packages/shopify-app-remix/src/server/authenticate/public/appProxy/__tests__/authenticate.test.ts @@ -215,10 +215,9 @@ describe('authenticating app proxy requests', () => { describe('Valid requests with a session return an admin API client', () => { expectAdminApiClient(async () => { const shopify = shopifyApp(testConfig()); - const expectedSession = await setUpValidSession( - shopify.sessionStorage, - false, - ); + const expectedSession = await setUpValidSession(shopify.sessionStorage, { + isOnline: false, + }); const {admin, session: actualSession} = await shopify.authenticate.public.appProxy(await getValidRequest()); @@ -234,10 +233,9 @@ describe('authenticating app proxy requests', () => { describe('Valid requests with a session return a Storefront API client', () => { expectStorefrontApiClient(async () => { const shopify = shopifyApp(testConfig()); - const expectedSession = await setUpValidSession( - shopify.sessionStorage, - false, - ); + const expectedSession = await setUpValidSession(shopify.sessionStorage, { + isOnline: false, + }); const {storefront, session: actualSession} = await shopify.authenticate.public.appProxy(await getValidRequest()); diff --git a/packages/shopify-app-remix/src/server/authenticate/webhooks/__tests__/mock-responses.ts b/packages/shopify-app-remix/src/server/authenticate/webhooks/__tests__/mock-responses.ts index 5377ca670a..fd0533599b 100644 --- a/packages/shopify-app-remix/src/server/authenticate/webhooks/__tests__/mock-responses.ts +++ b/packages/shopify-app-remix/src/server/authenticate/webhooks/__tests__/mock-responses.ts @@ -32,3 +32,14 @@ export const HTTP_WEBHOOK_CREATE_ERROR_RESPONSE = { }, }, }; + +export const HTTP_WEBHOOK_THROTTLING_ERROR_RESPONSE = { + errors: [ + { + message: 'Throttled', + extensions: { + code: 'THROTTLED', + }, + }, + ], +}; diff --git a/packages/shopify-app-remix/src/server/authenticate/webhooks/__tests__/register.test.ts b/packages/shopify-app-remix/src/server/authenticate/webhooks/__tests__/register.test.ts index b3dfd241ad..c8cec6fdc4 100644 --- a/packages/shopify-app-remix/src/server/authenticate/webhooks/__tests__/register.test.ts +++ b/packages/shopify-app-remix/src/server/authenticate/webhooks/__tests__/register.test.ts @@ -1,4 +1,4 @@ -import {DeliveryMethod, Session} from '@shopify/shopify-api'; +import {DeliveryMethod, GraphqlQueryError, Session} from '@shopify/shopify-api'; import {shopifyApp} from '../../..'; import { @@ -110,4 +110,101 @@ describe('Webhook registration', () => { NOT_A_VALID_TOPIC: [expect.objectContaining({success: false})], }); }); + + // eslint-disable-next-line no-warning-comments + // TODO: Remove tests once we have a better solution to parallel afterAuth calls + it('logs throttling errors', async () => { + // GIVEN + const shopify = shopifyApp( + testConfig({ + webhooks: { + NOT_A_VALID_TOPIC: { + deliveryMethod: DeliveryMethod.Http, + callbackUrl: '/webhooks', + }, + }, + }), + ); + const session = new Session({ + id: `offline_${TEST_SHOP}`, + shop: TEST_SHOP, + isOnline: false, + state: 'test', + accessToken: 'totally_real_token', + }); + + await mockExternalRequests( + { + request: new Request(GRAPHQL_URL, { + method: 'POST', + body: 'webhookSubscriptions', + }), + response: new Response( + JSON.stringify(mockResponses.EMPTY_WEBHOOK_RESPONSE), + ), + }, + { + request: new Request(GRAPHQL_URL, { + method: 'POST', + body: 'webhookSubscriptionCreate', + }), + response: new Response( + JSON.stringify(mockResponses.HTTP_WEBHOOK_THROTTLING_ERROR_RESPONSE), + ), + }, + ); + + // WHEN + const results = await shopify.registerWebhooks({session}); + + // THEN + expect(results).toBeUndefined(); + }); + + it('throws other errors', async () => { + // GIVEN + const shopify = shopifyApp( + testConfig({ + webhooks: { + NOT_A_VALID_TOPIC: { + deliveryMethod: DeliveryMethod.Http, + callbackUrl: '/webhooks', + }, + }, + }), + ); + const session = new Session({ + id: `offline_${TEST_SHOP}`, + shop: TEST_SHOP, + isOnline: false, + state: 'test', + accessToken: 'totally_real_token', + }); + + await mockExternalRequests( + { + request: new Request(GRAPHQL_URL, { + method: 'POST', + body: 'webhookSubscriptions', + }), + response: new Response( + JSON.stringify(mockResponses.EMPTY_WEBHOOK_RESPONSE), + ), + }, + { + request: new Request(GRAPHQL_URL, { + method: 'POST', + body: 'webhookSubscriptionCreate', + }), + response: new Response( + JSON.stringify({errors: [{extensions: {code: 'FAILED_REQUEST'}}]}), + ), + }, + ); + + // THEN + expect(shopify.registerWebhooks({session})).rejects.toThrowError( + GraphqlQueryError, + ); + }); }); diff --git a/packages/shopify-app-remix/src/server/authenticate/webhooks/register.ts b/packages/shopify-app-remix/src/server/authenticate/webhooks/register.ts index 0acc99c6a7..ec69392a48 100644 --- a/packages/shopify-app-remix/src/server/authenticate/webhooks/register.ts +++ b/packages/shopify-app-remix/src/server/authenticate/webhooks/register.ts @@ -4,26 +4,45 @@ import type {RegisterWebhooksOptions} from './types'; export function registerWebhooksFactory({api, logger}: BasicParams) { return async function registerWebhooks({session}: RegisterWebhooksOptions) { - return api.webhooks.register({session}).then((response) => { - Object.entries(response).forEach(([topic, topicResults]) => { - topicResults.forEach(({success, ...rest}) => { - if (success) { - logger.debug('Registered webhook', { - topic, - shop: session.shop, - operation: rest.operation, - }); - } else { - logger.error('Failed to register webhook', { - topic, - shop: session.shop, - result: JSON.stringify(rest.result), - }); - } + return api.webhooks + .register({session}) + .then((response) => { + Object.entries(response).forEach(([topic, topicResults]) => { + topicResults.forEach(({success, ...rest}) => { + if (success) { + logger.debug('Registered webhook', { + topic, + shop: session.shop, + operation: rest.operation, + }); + } else { + logger.error('Failed to register webhook', { + topic, + shop: session.shop, + result: JSON.stringify(rest.result), + }); + } + }); }); - }); - return response; - }); + return response; + }) + .catch((error) => { + const graphQLErrors: {extensions: {code?: string}}[] = + error.body?.errors?.graphQLErrors || []; + + const throttled = graphQLErrors.find( + ({extensions: {code}}) => code === 'THROTTLED', + ); + + if (throttled) { + logger.error('Failed to register webhooks', { + shop: session.shop, + error: JSON.stringify(error), + }); + } else { + throw error; + } + }); }; } diff --git a/packages/shopify-app-remix/src/server/config-types.ts b/packages/shopify-app-remix/src/server/config-types.ts index 78e9b6a3b7..4f68ce8df6 100644 --- a/packages/shopify-app-remix/src/server/config-types.ts +++ b/packages/shopify-app-remix/src/server/config-types.ts @@ -11,6 +11,7 @@ import {SessionStorage} from '@shopify/shopify-app-session-storage'; import type {FutureFlagOptions, FutureFlags} from './future/flags'; import type {AppDistribution} from './types'; import type {AdminApiContext} from './clients'; +import {IdempotentPromiseHandler} from './authenticate/helpers/idempotent-promise-handler'; export interface AppConfigArg< Resources extends ShopifyRestResources = ShopifyRestResources, @@ -233,6 +234,7 @@ export interface AppConfig useOnlineTokens: boolean; hooks: HooksConfig; future: FutureFlags; + idempotentPromiseHandler: IdempotentPromiseHandler; } export interface AuthConfig { diff --git a/packages/shopify-app-remix/src/server/future/flags.ts b/packages/shopify-app-remix/src/server/future/flags.ts index 8b8f1ccfd7..e8d75841ea 100644 --- a/packages/shopify-app-remix/src/server/future/flags.ts +++ b/packages/shopify-app-remix/src/server/future/flags.ts @@ -14,6 +14,13 @@ export interface FutureFlags { * @default false */ v3_authenticatePublic?: boolean; + + /** + * When enabled, embedded apps will fetch access tokens via token exchange. This assumes app are using declarative scopes with Shopify managing installs. + * + * @default false + */ + unstable_newEmbeddedAuthStrategy?: boolean; } export type FutureFlagOptions = FutureFlags | undefined; diff --git a/packages/shopify-app-remix/src/server/shopify-app.ts b/packages/shopify-app-remix/src/server/shopify-app.ts index 4b55649a9b..b3f5ac187a 100644 --- a/packages/shopify-app-remix/src/server/shopify-app.ts +++ b/packages/shopify-app-remix/src/server/shopify-app.ts @@ -30,6 +30,8 @@ import {unauthenticatedAdminContextFactory} from './unauthenticated/admin'; import {authenticatePublicFactory} from './authenticate/public'; import {unauthenticatedStorefrontContextFactory} from './unauthenticated/storefront'; import {AuthCodeFlowStrategy} from './authenticate/admin/strategies/auth-code-flow'; +import {TokenExchangeStrategy} from './authenticate/admin/strategies/token-exchange'; +import {IdempotentPromiseHandler} from './authenticate/helpers/idempotent-promise-handler'; /** * Creates an object your app will use to interact with Shopify. @@ -66,10 +68,12 @@ export function shopifyApp< } const params: BasicParams = {api, config, logger}; - const oauth = new AuthCodeFlowStrategy(params); const authStrategy = authStrategyFactory({ ...params, - strategy: oauth, + strategy: + config.future.unstable_newEmbeddedAuthStrategy && config.isEmbeddedApp + ? new TokenExchangeStrategy(params) + : new AuthCodeFlowStrategy(params), }); const shopify: @@ -148,7 +152,10 @@ function deriveApi(appConfig: AppConfigArg) { isEmbeddedApp: appConfig.isEmbeddedApp ?? true, apiVersion: appConfig.apiVersion ?? LATEST_API_VERSION, isCustomStoreApp: appConfig.distribution === AppDistribution.ShopifyAdmin, - future: {}, + future: { + unstable_tokenExchange: + appConfig.future?.unstable_newEmbeddedAuthStrategy, + }, }); } @@ -168,6 +175,7 @@ function deriveConfig( return { ...appConfig, ...apiConfig, + idempotentPromiseHandler: new IdempotentPromiseHandler(), canUseLoginForm: appConfig.distribution !== AppDistribution.ShopifyAdmin, useOnlineTokens: appConfig.useOnlineTokens ?? false, hooks: appConfig.hooks ?? {}, diff --git a/packages/shopify-app-remix/src/server/types.ts b/packages/shopify-app-remix/src/server/types.ts index a219a8ee5a..e3cfaa55ac 100644 --- a/packages/shopify-app-remix/src/server/types.ts +++ b/packages/shopify-app-remix/src/server/types.ts @@ -1,4 +1,5 @@ import { + ConfigParams, RegisterReturn, Shopify, ShopifyRestResources, @@ -13,13 +14,29 @@ import type { import type {AuthenticatePublic} from './authenticate/public/types'; import type {AdminContext} from './authenticate/admin/types'; import type {Unauthenticated} from './unauthenticated/types'; +import {FutureFlagOptions, FutureFlags} from './future/flags'; -export interface BasicParams { - api: Shopify; +export interface BasicParams< + Future extends FutureFlagOptions = FutureFlagOptions, +> { + api: Shopify< + ApiConfigWithFutureFlags, + ShopifyRestResources, + ApiFutureFlags + >; config: AppConfig; logger: Shopify['logger']; } +export interface ApiFutureFlags { + unstable_tokenExchange?: Future extends FutureFlags + ? Future['unstable_newEmbeddedAuthStrategy'] + : boolean; +} + +export type ApiConfigWithFutureFlags = + ConfigParams>; + export type JSONValue = | string | number @@ -49,7 +66,7 @@ interface JSONArray extends Array {} type RegisterWebhooks = ( options: RegisterWebhooksOptions, -) => Promise; +) => Promise; export enum LoginErrorType { MissingShop = 'MISSING_SHOP', diff --git a/packages/shopify-app-remix/src/server/unauthenticated/admin/__tests__/factory.test.ts b/packages/shopify-app-remix/src/server/unauthenticated/admin/__tests__/factory.test.ts index 7e74ec6280..c7cbe53ef0 100644 --- a/packages/shopify-app-remix/src/server/unauthenticated/admin/__tests__/factory.test.ts +++ b/packages/shopify-app-remix/src/server/unauthenticated/admin/__tests__/factory.test.ts @@ -17,10 +17,9 @@ describe('unauthenticated admin context', () => { expectAdminApiClient(async () => { const shopify = shopifyApp(testConfig()); - const expectedSession = await setUpValidSession( - shopify.sessionStorage, - false, - ); + const expectedSession = await setUpValidSession(shopify.sessionStorage, { + isOnline: false, + }); const {admin, session: actualSession} = await shopify.unauthenticated.admin( TEST_SHOP, ); diff --git a/packages/shopify-app-remix/src/server/unauthenticated/storefront/__tests__/factory.test.ts b/packages/shopify-app-remix/src/server/unauthenticated/storefront/__tests__/factory.test.ts index 5ac01e34e6..c747ab4d25 100644 --- a/packages/shopify-app-remix/src/server/unauthenticated/storefront/__tests__/factory.test.ts +++ b/packages/shopify-app-remix/src/server/unauthenticated/storefront/__tests__/factory.test.ts @@ -19,10 +19,9 @@ describe('unauthenticated storefront context', () => { expectStorefrontApiClient(async () => { const shopify = shopifyApp(testConfig()); - const expectedSession = await setUpValidSession( - shopify.sessionStorage, - false, - ); + const expectedSession = await setUpValidSession(shopify.sessionStorage, { + isOnline: false, + }); const {storefront, session: actualSession} = await shopify.unauthenticated.storefront(TEST_SHOP); diff --git a/packages/shopify-app-session-storage-dynamodb/package.json b/packages/shopify-app-session-storage-dynamodb/package.json index fa3d3165b0..24fd74cb13 100644 --- a/packages/shopify-app-session-storage-dynamodb/package.json +++ b/packages/shopify-app-session-storage-dynamodb/package.json @@ -33,23 +33,14 @@ ], "dependencies": { "@aws-sdk/client-dynamodb": "^3.470.0", - "@aws-sdk/util-dynamodb": "^3.490.0", - "tslib": "^2.6.2" + "@aws-sdk/util-dynamodb": "^3.490.0" }, "peerDependencies": { - "@shopify/shopify-api": "^9.0.1", + "@shopify/shopify-api": "^9.0.2", "@shopify/shopify-app-session-storage": "^2.0.3" }, "devDependencies": { - "@shopify/eslint-plugin": "^42.1.0", - "@shopify/prettier-config": "^1.1.2", - "@shopify/shopify-api": "^9.0.1", - "@shopify/shopify-app-session-storage": "^2.0.3", - "@shopify/shopify-app-session-storage-test-utils": "^1.0.3", - "eslint": "^8.55.0", - "eslint-plugin-prettier": "^4.2.1", - "prettier": "^2.8.8", - "typescript": "^4.9.5" + "@shopify/shopify-app-session-storage-test-utils": "^1.0.3" }, "files": [ "build/*", diff --git a/packages/shopify-app-session-storage-kv/package.json b/packages/shopify-app-session-storage-kv/package.json index 129f86fe7a..fe2e8ddef7 100644 --- a/packages/shopify-app-session-storage-kv/package.json +++ b/packages/shopify-app-session-storage-kv/package.json @@ -32,23 +32,16 @@ "CloudFlare" ], "dependencies": { - "semver": "^7.5.4", - "tslib": "^2.6.2" + "semver": "^7.5.4" }, "peerDependencies": { - "@shopify/shopify-api": "^9.0.1", + "@shopify/shopify-api": "^9.0.2", "@shopify/shopify-app-session-storage": "^2.0.3" }, "devDependencies": { "@cloudflare/workers-types": "^4.20230511.0", - "@shopify/eslint-plugin": "^42.1.0", - "@shopify/prettier-config": "^1.1.2", "@shopify/shopify-app-session-storage-test-utils": "^1.0.3", - "eslint": "^8.55.0", - "eslint-plugin-prettier": "^4.2.1", - "miniflare": "^2.14.0", - "prettier": "^2.8.8", - "typescript": "^4.9.5" + "miniflare": "^2.14.0" }, "files": [ "build/*", diff --git a/packages/shopify-app-session-storage-memory/package.json b/packages/shopify-app-session-storage-memory/package.json index d568d00b03..7c3ecb5b67 100644 --- a/packages/shopify-app-session-storage-memory/package.json +++ b/packages/shopify-app-session-storage-memory/package.json @@ -29,22 +29,13 @@ "Storefront API", "session storage" ], - "dependencies": { - "tslib": "^2.6.2" - }, + "dependencies": {}, "peerDependencies": { - "@shopify/shopify-api": "^9.0.1", + "@shopify/shopify-api": "^9.0.2", "@shopify/shopify-app-session-storage": "^2.0.3" }, "devDependencies": { - "@shopify/eslint-plugin": "^42.1.0", - "@shopify/prettier-config": "^1.1.2", - "@shopify/shopify-app-session-storage-test-utils": "^1.0.3", - "@types/jest": "^29.5.1", - "eslint": "^8.55.0", - "eslint-plugin-prettier": "^4.2.1", - "prettier": "^2.8.8", - "typescript": "4.9.5" + "@shopify/shopify-app-session-storage-test-utils": "^1.0.3" }, "files": [ "build/*", diff --git a/packages/shopify-app-session-storage-mongodb/package.json b/packages/shopify-app-session-storage-mongodb/package.json index 8f9b7ff615..8ddf70626d 100644 --- a/packages/shopify-app-session-storage-mongodb/package.json +++ b/packages/shopify-app-session-storage-mongodb/package.json @@ -31,22 +31,14 @@ "session storage" ], "dependencies": { - "mongodb": "^6.3.0", - "tslib": "^2.6.2" + "mongodb": "^6.3.0" }, "peerDependencies": { - "@shopify/shopify-api": "^9.0.1", + "@shopify/shopify-api": "^9.0.2", "@shopify/shopify-app-session-storage": "^2.0.3" }, "devDependencies": { - "@shopify/eslint-plugin": "^42.1.0", - "@shopify/prettier-config": "^1.1.2", - "@shopify/shopify-app-session-storage-test-utils": "^1.0.3", - "@types/jest": "^29.5.1", - "eslint": "^8.55.0", - "eslint-plugin-prettier": "^4.2.1", - "prettier": "^2.8.8", - "typescript": "4.9.5" + "@shopify/shopify-app-session-storage-test-utils": "^1.0.3" }, "files": [ "build/*", diff --git a/packages/shopify-app-session-storage-mysql/package.json b/packages/shopify-app-session-storage-mysql/package.json index 6510f8a373..eb2e51d7a1 100644 --- a/packages/shopify-app-session-storage-mysql/package.json +++ b/packages/shopify-app-session-storage-mysql/package.json @@ -32,22 +32,14 @@ "session storage" ], "dependencies": { - "mysql2": "^3.3.1", - "tslib": "^2.6.2" + "mysql2": "^3.3.1" }, "peerDependencies": { - "@shopify/shopify-api": "^9.0.1", + "@shopify/shopify-api": "^9.0.2", "@shopify/shopify-app-session-storage": "^2.0.3" }, "devDependencies": { - "@shopify/eslint-plugin": "^42.1.0", - "@shopify/prettier-config": "^1.1.2", - "@shopify/shopify-app-session-storage-test-utils": "^1.0.3", - "@types/jest": "^29.5.1", - "eslint": "^8.55.0", - "eslint-plugin-prettier": "^4.2.1", - "prettier": "^2.8.8", - "typescript": "4.9.5" + "@shopify/shopify-app-session-storage-test-utils": "^1.0.3" }, "files": [ "build/*", diff --git a/packages/shopify-app-session-storage-postgresql/package.json b/packages/shopify-app-session-storage-postgresql/package.json index 6aa8836e64..2201625957 100644 --- a/packages/shopify-app-session-storage-postgresql/package.json +++ b/packages/shopify-app-session-storage-postgresql/package.json @@ -33,23 +33,15 @@ ], "dependencies": { "pg": "^8.11.0", - "pg-connection-string": "^2.6.2", - "tslib": "^2.6.2" + "pg-connection-string": "^2.6.2" }, "peerDependencies": { - "@shopify/shopify-api": "^9.0.1", + "@shopify/shopify-api": "^9.0.2", "@shopify/shopify-app-session-storage": "^2.0.3" }, "devDependencies": { - "@shopify/eslint-plugin": "^42.1.0", - "@shopify/prettier-config": "^1.1.2", "@shopify/shopify-app-session-storage-test-utils": "^1.0.3", - "@types/jest": "^29.5.1", - "@types/pg": "^8.6.6", - "eslint": "^8.55.0", - "eslint-plugin-prettier": "^4.2.1", - "prettier": "^2.8.8", - "typescript": "4.9.5" + "@types/pg": "^8.6.6" }, "files": [ "build/*", diff --git a/packages/shopify-app-session-storage-prisma/package.json b/packages/shopify-app-session-storage-prisma/package.json index 9e23a0365b..61e317771f 100644 --- a/packages/shopify-app-session-storage-prisma/package.json +++ b/packages/shopify-app-session-storage-prisma/package.json @@ -30,27 +30,17 @@ "session storage", "Prisma" ], - "dependencies": { - "tslib": "^2.6.2" - }, + "dependencies": {}, "peerDependencies": { - "@prisma/client": "^4.13.0", - "@shopify/shopify-api": "^9.0.1", + "@prisma/client": "^5.8.1", + "@shopify/shopify-api": "^9.0.2", "@shopify/shopify-app-session-storage": "^2.0.3", - "prisma": "^4.13.0" + "prisma": "^5.8.1" }, "devDependencies": { - "@prisma/client": "^4.13.0", - "@shopify/shopify-api": "^9.0.1", - "@shopify/shopify-app-session-storage": "^2.0.3", - "prisma": "^4.13.0", - "@shopify/eslint-plugin": "^42.1.0", - "@shopify/prettier-config": "^1.1.2", + "@prisma/client": "^5.8.1", "@shopify/shopify-app-session-storage-test-utils": "^1.0.3", - "eslint": "^8.55.0", - "eslint-plugin-prettier": "^4.2.1", - "prettier": "^2.8.7", - "typescript": "4.9.5" + "prisma": "^5.8.1" }, "files": [ "build/*", diff --git a/packages/shopify-app-session-storage-redis/package.json b/packages/shopify-app-session-storage-redis/package.json index 8c322e5e60..d5c30d0807 100644 --- a/packages/shopify-app-session-storage-redis/package.json +++ b/packages/shopify-app-session-storage-redis/package.json @@ -32,22 +32,14 @@ "Redis" ], "dependencies": { - "redis": "^4.6.11", - "tslib": "^2.6.2" + "redis": "^4.6.11" }, "peerDependencies": { - "@shopify/shopify-api": "^9.0.1", + "@shopify/shopify-api": "^9.0.2", "@shopify/shopify-app-session-storage": "^2.0.3" }, "devDependencies": { - "@shopify/eslint-plugin": "^42.1.0", - "@shopify/prettier-config": "^1.1.2", - "@shopify/shopify-app-session-storage-test-utils": "^1.0.3", - "@types/jest": "^29.5.1", - "eslint": "^8.55.0", - "eslint-plugin-prettier": "^4.2.1", - "prettier": "^2.8.8", - "typescript": "4.9.5" + "@shopify/shopify-app-session-storage-test-utils": "^1.0.3" }, "files": [ "build/*", diff --git a/packages/shopify-app-session-storage-sqlite/package.json b/packages/shopify-app-session-storage-sqlite/package.json index 9b29613350..84976a9c4a 100644 --- a/packages/shopify-app-session-storage-sqlite/package.json +++ b/packages/shopify-app-session-storage-sqlite/package.json @@ -32,23 +32,15 @@ "sqlite" ], "dependencies": { - "sqlite3": "^5.1.6", - "tslib": "^2.6.2" + "sqlite3": "^5.1.6" }, "peerDependencies": { - "@shopify/shopify-api": "^9.0.1", + "@shopify/shopify-api": "^9.0.2", "@shopify/shopify-app-session-storage": "^2.0.3" }, "devDependencies": { - "@shopify/eslint-plugin": "^42.1.0", - "@shopify/prettier-config": "^1.1.2", "@shopify/shopify-app-session-storage-test-utils": "^1.0.3", - "@types/jest": "^29.5.1", - "@types/sqlite3": "^3.1.8", - "eslint": "^8.55.0", - "eslint-plugin-prettier": "^4.2.1", - "prettier": "^2.8.8", - "typescript": "4.9.5" + "@types/sqlite3": "^3.1.8" }, "files": [ "build/*", diff --git a/packages/shopify-app-session-storage-test-utils/package.json b/packages/shopify-app-session-storage-test-utils/package.json index e90cd2d1f0..a67d2fe7f1 100644 --- a/packages/shopify-app-session-storage-test-utils/package.json +++ b/packages/shopify-app-session-storage-test-utils/package.json @@ -33,27 +33,12 @@ "session storage", "test utilities" ], - "dependencies": { - "jest-fetch-mock": "^3.0.3", - "tslib": "^2.6.2" - }, + "dependencies": {}, "peerDependencies": { - "@shopify/shopify-api": "^9.0.1", + "@shopify/shopify-api": "^9.0.2", "@shopify/shopify-app-session-storage": "^2.0.3" }, - "devDependencies": { - "@shopify/eslint-plugin": "^42.1.0", - "@shopify/prettier-config": "^1.1.2", - "@types/jest": "^29.5.1", - "eslint": "^8.55.0", - "eslint-plugin-prettier": "^4.2.1", - "jest": "^29.5.0", - "jest-fetch-mock": "^3.0.3", - "jest-runner-eslint": "^2.0.0", - "prettier": "^2.8.8", - "ts-jest": "^29.1.0", - "typescript": "^4.9.5" - }, + "devDependencies": {}, "files": [ "build/*", "!bundle/*", diff --git a/packages/shopify-app-session-storage/package.json b/packages/shopify-app-session-storage/package.json index f3e01f870a..2c66a731a2 100644 --- a/packages/shopify-app-session-storage/package.json +++ b/packages/shopify-app-session-storage/package.json @@ -30,21 +30,11 @@ "Storefront API", "session storage" ], - "dependencies": { - "tslib": "^2.6.2" - }, + "dependencies": {}, "peerDependencies": { - "@shopify/shopify-api": "^9.0.1" - }, - "devDependencies": { - "@shopify/eslint-plugin": "^42.1.0", - "@shopify/prettier-config": "^1.1.2", - "@types/jest": "^29.5.1", - "eslint": "^8.55.0", - "eslint-plugin-prettier": "^4.2.1", - "prettier": "^2.8.8", - "typescript": "^4.9.5" + "@shopify/shopify-api": "^9.0.2" }, + "devDependencies": {}, "files": [ "build/*", "!bundle/*", diff --git a/yarn.lock b/yarn.lock index 39b083ec93..39a80b221b 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1567,14 +1567,7 @@ resolved "https://registry.yarnpkg.com/@babel/regjsgen/-/regjsgen-0.8.0.tgz#f0ba69b075e1f05fb2825b7fad991e7adbb18310" integrity sha512-x/rqGMdzj+fWZvCOYForTghzbtqPDZ5gPwaoNGHdgDfF2QA/XZbCBp4Moo5scrkAMPhB7z26XM/AaHuIJdgauA== -"@babel/runtime@^7.16.3", "@babel/runtime@^7.20.1", "@babel/runtime@^7.20.7", "@babel/runtime@^7.5.5", "@babel/runtime@^7.8.4": - version "7.21.5" - resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.21.5.tgz#8492dddda9644ae3bda3b45eabe87382caee7200" - integrity sha512-8jI69toZqqcsnqGGqwGS4Qb1VwLOEp4hz+CXPywcvjs60u3B4Pom/U/7rm4W8tMOYEB+E9wgD0mW1l3r8qlI9Q== - dependencies: - regenerator-runtime "^0.13.11" - -"@babel/runtime@^7.8.7": +"@babel/runtime@^7.16.3", "@babel/runtime@^7.20.1", "@babel/runtime@^7.20.7", "@babel/runtime@^7.5.5", "@babel/runtime@^7.8.4", "@babel/runtime@^7.8.7": version "7.22.6" resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.22.6.tgz#57d64b9ae3cff1d67eb067ae117dac087f5bd438" integrity sha512-wDb5pWm4WDdF6LFUde3Jl8WzPA+3ZbxYqkC6xAXuD3irdEHN1k0NfTRrJD8ZD378SJ61miMLCqIOXYhd8x+AJQ== @@ -1638,16 +1631,16 @@ resolved "https://registry.yarnpkg.com/@bcoe/v8-coverage/-/v8-coverage-0.2.3.tgz#75a2e8b51cb758a7553d6804a5932d7aace75c39" integrity sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw== -"@changesets/apply-release-plan@^6.1.3": - version "6.1.3" - resolved "https://registry.yarnpkg.com/@changesets/apply-release-plan/-/apply-release-plan-6.1.3.tgz#3bcc0bd57ba00d50d20df7d0141f1a9b2134eaf7" - integrity sha512-ECDNeoc3nfeAe1jqJb5aFQX7CqzQhD2klXRez2JDb/aVpGUbX673HgKrnrgJRuQR/9f2TtLoYIzrGB9qwD77mg== +"@changesets/apply-release-plan@^7.0.0": + version "7.0.0" + resolved "https://registry.yarnpkg.com/@changesets/apply-release-plan/-/apply-release-plan-7.0.0.tgz#ce3c3dfc5720550a5d592b54ad2f411f816ec5ff" + integrity sha512-vfi69JR416qC9hWmFGSxj7N6wA5J222XNBmezSVATPWDVPIF7gkd4d8CpbEbXmRWbVrkoli3oerGS6dcL/BGsQ== dependencies: "@babel/runtime" "^7.20.1" - "@changesets/config" "^2.3.0" - "@changesets/get-version-range-type" "^0.3.2" - "@changesets/git" "^2.0.0" - "@changesets/types" "^5.2.1" + "@changesets/config" "^3.0.0" + "@changesets/get-version-range-type" "^0.4.0" + "@changesets/git" "^3.0.0" + "@changesets/types" "^6.0.0" "@manypkg/get-packages" "^1.1.3" detect-indent "^6.0.0" fs-extra "^7.0.1" @@ -1655,164 +1648,163 @@ outdent "^0.5.0" prettier "^2.7.1" resolve-from "^5.0.0" - semver "^5.4.1" + semver "^7.5.3" -"@changesets/assemble-release-plan@^5.2.3": - version "5.2.3" - resolved "https://registry.yarnpkg.com/@changesets/assemble-release-plan/-/assemble-release-plan-5.2.3.tgz#5ce6191c6e193d40b566a7b0e01690cfb106f4db" - integrity sha512-g7EVZCmnWz3zMBAdrcKhid4hkHT+Ft1n0mLussFMcB1dE2zCuwcvGoy9ec3yOgPGF4hoMtgHaMIk3T3TBdvU9g== +"@changesets/assemble-release-plan@^6.0.0": + version "6.0.0" + resolved "https://registry.yarnpkg.com/@changesets/assemble-release-plan/-/assemble-release-plan-6.0.0.tgz#c69969b4bef7c32a8544b6941d1053260ca47e05" + integrity sha512-4QG7NuisAjisbW4hkLCmGW2lRYdPrKzro+fCtZaILX+3zdUELSvYjpL4GTv0E4aM9Mef3PuIQp89VmHJ4y2bfw== dependencies: "@babel/runtime" "^7.20.1" - "@changesets/errors" "^0.1.4" - "@changesets/get-dependents-graph" "^1.3.5" - "@changesets/types" "^5.2.1" + "@changesets/errors" "^0.2.0" + "@changesets/get-dependents-graph" "^2.0.0" + "@changesets/types" "^6.0.0" "@manypkg/get-packages" "^1.1.3" - semver "^5.4.1" + semver "^7.5.3" -"@changesets/changelog-git@^0.1.14": - version "0.1.14" - resolved "https://registry.yarnpkg.com/@changesets/changelog-git/-/changelog-git-0.1.14.tgz#852caa7727dcf91497c131d05bc2cd6248532ada" - integrity sha512-+vRfnKtXVWsDDxGctOfzJsPhaCdXRYoe+KyWYoq5X/GqoISREiat0l3L8B0a453B2B4dfHGcZaGyowHbp9BSaA== +"@changesets/changelog-git@^0.2.0": + version "0.2.0" + resolved "https://registry.yarnpkg.com/@changesets/changelog-git/-/changelog-git-0.2.0.tgz#1f3de11becafff5a38ebe295038a602403c93a86" + integrity sha512-bHOx97iFI4OClIT35Lok3sJAwM31VbUM++gnMBV16fdbtBhgYu4dxsphBF/0AZZsyAHMrnM0yFcj5gZM1py6uQ== dependencies: - "@changesets/types" "^5.2.1" + "@changesets/types" "^6.0.0" -"@changesets/cli@^2.26.1": - version "2.26.1" - resolved "https://registry.yarnpkg.com/@changesets/cli/-/cli-2.26.1.tgz#2d10858d7d32314a524e383111c96d831eb0402f" - integrity sha512-XnTa+b51vt057fyAudvDKGB0Sh72xutQZNAdXkCqPBKO2zvs2yYZx5hFZj1u9cbtpwM6Sxtcr02/FQJfZOzemQ== +"@changesets/cli@^2.27.1": + version "2.27.1" + resolved "https://registry.yarnpkg.com/@changesets/cli/-/cli-2.27.1.tgz#abce480fd30b9abbe2cfcf07d5d668c364ce2804" + integrity sha512-iJ91xlvRnnrJnELTp4eJJEOPjgpF3NOh4qeQehM6Ugiz9gJPRZ2t+TsXun6E3AMN4hScZKjqVXl0TX+C7AB3ZQ== dependencies: "@babel/runtime" "^7.20.1" - "@changesets/apply-release-plan" "^6.1.3" - "@changesets/assemble-release-plan" "^5.2.3" - "@changesets/changelog-git" "^0.1.14" - "@changesets/config" "^2.3.0" - "@changesets/errors" "^0.1.4" - "@changesets/get-dependents-graph" "^1.3.5" - "@changesets/get-release-plan" "^3.0.16" - "@changesets/git" "^2.0.0" - "@changesets/logger" "^0.0.5" - "@changesets/pre" "^1.0.14" - "@changesets/read" "^0.5.9" - "@changesets/types" "^5.2.1" - "@changesets/write" "^0.2.3" + "@changesets/apply-release-plan" "^7.0.0" + "@changesets/assemble-release-plan" "^6.0.0" + "@changesets/changelog-git" "^0.2.0" + "@changesets/config" "^3.0.0" + "@changesets/errors" "^0.2.0" + "@changesets/get-dependents-graph" "^2.0.0" + "@changesets/get-release-plan" "^4.0.0" + "@changesets/git" "^3.0.0" + "@changesets/logger" "^0.1.0" + "@changesets/pre" "^2.0.0" + "@changesets/read" "^0.6.0" + "@changesets/types" "^6.0.0" + "@changesets/write" "^0.3.0" "@manypkg/get-packages" "^1.1.3" - "@types/is-ci" "^3.0.0" - "@types/semver" "^6.0.0" + "@types/semver" "^7.5.0" ansi-colors "^4.1.3" chalk "^2.1.0" + ci-info "^3.7.0" enquirer "^2.3.0" external-editor "^3.1.0" fs-extra "^7.0.1" human-id "^1.0.2" - is-ci "^3.0.1" meow "^6.0.0" outdent "^0.5.0" p-limit "^2.2.0" preferred-pm "^3.0.0" resolve-from "^5.0.0" - semver "^5.4.1" + semver "^7.5.3" spawndamnit "^2.0.0" term-size "^2.1.0" tty-table "^4.1.5" -"@changesets/config@^2.3.0": - version "2.3.0" - resolved "https://registry.yarnpkg.com/@changesets/config/-/config-2.3.0.tgz#bff074d6492fa772cee139f9a04efa4cd56445bb" - integrity sha512-EgP/px6mhCx8QeaMAvWtRrgyxW08k/Bx2tpGT+M84jEdX37v3VKfh4Cz1BkwrYKuMV2HZKeHOh8sHvja/HcXfQ== +"@changesets/config@^3.0.0": + version "3.0.0" + resolved "https://registry.yarnpkg.com/@changesets/config/-/config-3.0.0.tgz#a1a1cafc77134b117b4a9266459c84fdd360a6be" + integrity sha512-o/rwLNnAo/+j9Yvw9mkBQOZySDYyOr/q+wptRLcAVGlU6djOeP9v1nlalbL9MFsobuBVQbZCTp+dIzdq+CLQUA== dependencies: - "@changesets/errors" "^0.1.4" - "@changesets/get-dependents-graph" "^1.3.5" - "@changesets/logger" "^0.0.5" - "@changesets/types" "^5.2.1" + "@changesets/errors" "^0.2.0" + "@changesets/get-dependents-graph" "^2.0.0" + "@changesets/logger" "^0.1.0" + "@changesets/types" "^6.0.0" "@manypkg/get-packages" "^1.1.3" fs-extra "^7.0.1" micromatch "^4.0.2" -"@changesets/errors@^0.1.4": - version "0.1.4" - resolved "https://registry.yarnpkg.com/@changesets/errors/-/errors-0.1.4.tgz#f79851746c43679a66b383fdff4c012f480f480d" - integrity sha512-HAcqPF7snsUJ/QzkWoKfRfXushHTu+K5KZLJWPb34s4eCZShIf8BFO3fwq6KU8+G7L5KdtN2BzQAXOSXEyiY9Q== +"@changesets/errors@^0.2.0": + version "0.2.0" + resolved "https://registry.yarnpkg.com/@changesets/errors/-/errors-0.2.0.tgz#3c545e802b0f053389cadcf0ed54e5636ff9026a" + integrity sha512-6BLOQUscTpZeGljvyQXlWOItQyU71kCdGz7Pi8H8zdw6BI0g3m43iL4xKUVPWtG+qrrL9DTjpdn8eYuCQSRpow== dependencies: extendable-error "^0.1.5" -"@changesets/get-dependents-graph@^1.3.5": - version "1.3.5" - resolved "https://registry.yarnpkg.com/@changesets/get-dependents-graph/-/get-dependents-graph-1.3.5.tgz#f94c6672d2f9a87aa35512eea74550585ba41c21" - integrity sha512-w1eEvnWlbVDIY8mWXqWuYE9oKhvIaBhzqzo4ITSJY9hgoqQ3RoBqwlcAzg11qHxv/b8ReDWnMrpjpKrW6m1ZTA== +"@changesets/get-dependents-graph@^2.0.0": + version "2.0.0" + resolved "https://registry.yarnpkg.com/@changesets/get-dependents-graph/-/get-dependents-graph-2.0.0.tgz#97f0cc9fbec436e0d6ab95a6a59c08acf21ac714" + integrity sha512-cafUXponivK4vBgZ3yLu944mTvam06XEn2IZGjjKc0antpenkYANXiiE6GExV/yKdsCnE8dXVZ25yGqLYZmScA== dependencies: - "@changesets/types" "^5.2.1" + "@changesets/types" "^6.0.0" "@manypkg/get-packages" "^1.1.3" chalk "^2.1.0" fs-extra "^7.0.1" - semver "^5.4.1" + semver "^7.5.3" -"@changesets/get-release-plan@^3.0.16": - version "3.0.16" - resolved "https://registry.yarnpkg.com/@changesets/get-release-plan/-/get-release-plan-3.0.16.tgz#5d9cfc4ffda02c496ef0fde407210de8e3a0fb19" - integrity sha512-OpP9QILpBp1bY2YNIKFzwigKh7Qe9KizRsZomzLe6pK8IUo8onkAAVUD8+JRKSr8R7d4+JRuQrfSSNlEwKyPYg== +"@changesets/get-release-plan@^4.0.0": + version "4.0.0" + resolved "https://registry.yarnpkg.com/@changesets/get-release-plan/-/get-release-plan-4.0.0.tgz#8cb057da90a08796a335dfd18073234d33902069" + integrity sha512-9L9xCUeD/Tb6L/oKmpm8nyzsOzhdNBBbt/ZNcjynbHC07WW4E1eX8NMGC5g5SbM5z/V+MOrYsJ4lRW41GCbg3w== dependencies: "@babel/runtime" "^7.20.1" - "@changesets/assemble-release-plan" "^5.2.3" - "@changesets/config" "^2.3.0" - "@changesets/pre" "^1.0.14" - "@changesets/read" "^0.5.9" - "@changesets/types" "^5.2.1" + "@changesets/assemble-release-plan" "^6.0.0" + "@changesets/config" "^3.0.0" + "@changesets/pre" "^2.0.0" + "@changesets/read" "^0.6.0" + "@changesets/types" "^6.0.0" "@manypkg/get-packages" "^1.1.3" -"@changesets/get-version-range-type@^0.3.2": - version "0.3.2" - resolved "https://registry.yarnpkg.com/@changesets/get-version-range-type/-/get-version-range-type-0.3.2.tgz#8131a99035edd11aa7a44c341cbb05e668618c67" - integrity sha512-SVqwYs5pULYjYT4op21F2pVbcrca4qA/bAA3FmFXKMN7Y+HcO8sbZUTx3TAy2VXulP2FACd1aC7f2nTuqSPbqg== +"@changesets/get-version-range-type@^0.4.0": + version "0.4.0" + resolved "https://registry.yarnpkg.com/@changesets/get-version-range-type/-/get-version-range-type-0.4.0.tgz#429a90410eefef4368502c41c63413e291740bf5" + integrity sha512-hwawtob9DryoGTpixy1D3ZXbGgJu1Rhr+ySH2PvTLHvkZuQ7sRT4oQwMh0hbqZH1weAooedEjRsbrWcGLCeyVQ== -"@changesets/git@^2.0.0": - version "2.0.0" - resolved "https://registry.yarnpkg.com/@changesets/git/-/git-2.0.0.tgz#8de57649baf13a86eb669a25fa51bcad5cea517f" - integrity sha512-enUVEWbiqUTxqSnmesyJGWfzd51PY4H7mH9yUw0hPVpZBJ6tQZFMU3F3mT/t9OJ/GjyiM4770i+sehAn6ymx6A== +"@changesets/git@^3.0.0": + version "3.0.0" + resolved "https://registry.yarnpkg.com/@changesets/git/-/git-3.0.0.tgz#e71d003752a97bc27988db6d410e0038a4a88055" + integrity sha512-vvhnZDHe2eiBNRFHEgMiGd2CT+164dfYyrJDhwwxTVD/OW0FUD6G7+4DIx1dNwkwjHyzisxGAU96q0sVNBns0w== dependencies: "@babel/runtime" "^7.20.1" - "@changesets/errors" "^0.1.4" - "@changesets/types" "^5.2.1" + "@changesets/errors" "^0.2.0" + "@changesets/types" "^6.0.0" "@manypkg/get-packages" "^1.1.3" is-subdir "^1.1.1" micromatch "^4.0.2" spawndamnit "^2.0.0" -"@changesets/logger@^0.0.5": - version "0.0.5" - resolved "https://registry.yarnpkg.com/@changesets/logger/-/logger-0.0.5.tgz#68305dd5a643e336be16a2369cb17cdd8ed37d4c" - integrity sha512-gJyZHomu8nASHpaANzc6bkQMO9gU/ib20lqew1rVx753FOxffnCrJlGIeQVxNWCqM+o6OOleCo/ivL8UAO5iFw== +"@changesets/logger@^0.1.0": + version "0.1.0" + resolved "https://registry.yarnpkg.com/@changesets/logger/-/logger-0.1.0.tgz#2d2a58536c5beeeaef52ab464931d99fcf24f17b" + integrity sha512-pBrJm4CQm9VqFVwWnSqKEfsS2ESnwqwH+xR7jETxIErZcfd1u2zBSqrHbRHR7xjhSgep9x2PSKFKY//FAshA3g== dependencies: chalk "^2.1.0" -"@changesets/parse@^0.3.16": - version "0.3.16" - resolved "https://registry.yarnpkg.com/@changesets/parse/-/parse-0.3.16.tgz#f8337b70aeb476dc81745ab3294022909bc4a84a" - integrity sha512-127JKNd167ayAuBjUggZBkmDS5fIKsthnr9jr6bdnuUljroiERW7FBTDNnNVyJ4l69PzR57pk6mXQdtJyBCJKg== +"@changesets/parse@^0.4.0": + version "0.4.0" + resolved "https://registry.yarnpkg.com/@changesets/parse/-/parse-0.4.0.tgz#5cabbd9844b3b213cb83f5edb5768454c70dd2b4" + integrity sha512-TS/9KG2CdGXS27S+QxbZXgr8uPsP4yNJYb4BC2/NeFUj80Rni3TeD2qwWmabymxmrLo7JEsytXH1FbpKTbvivw== dependencies: - "@changesets/types" "^5.2.1" + "@changesets/types" "^6.0.0" js-yaml "^3.13.1" -"@changesets/pre@^1.0.14": - version "1.0.14" - resolved "https://registry.yarnpkg.com/@changesets/pre/-/pre-1.0.14.tgz#9df73999a4d15804da7381358d77bb37b00ddf0f" - integrity sha512-dTsHmxQWEQekHYHbg+M1mDVYFvegDh9j/kySNuDKdylwfMEevTeDouR7IfHNyVodxZXu17sXoJuf2D0vi55FHQ== +"@changesets/pre@^2.0.0": + version "2.0.0" + resolved "https://registry.yarnpkg.com/@changesets/pre/-/pre-2.0.0.tgz#ad3edf3d6ac287991d7ef5e26cf280d03c9e3764" + integrity sha512-HLTNYX/A4jZxc+Sq8D1AMBsv+1qD6rmmJtjsCJa/9MSRybdxh0mjbTvE6JYZQ/ZiQ0mMlDOlGPXTm9KLTU3jyw== dependencies: "@babel/runtime" "^7.20.1" - "@changesets/errors" "^0.1.4" - "@changesets/types" "^5.2.1" + "@changesets/errors" "^0.2.0" + "@changesets/types" "^6.0.0" "@manypkg/get-packages" "^1.1.3" fs-extra "^7.0.1" -"@changesets/read@^0.5.9": - version "0.5.9" - resolved "https://registry.yarnpkg.com/@changesets/read/-/read-0.5.9.tgz#a1b63a82b8e9409738d7a0f9cc39b6d7c28cbab0" - integrity sha512-T8BJ6JS6j1gfO1HFq50kU3qawYxa4NTbI/ASNVVCBTsKquy2HYwM9r7ZnzkiMe8IEObAJtUVGSrePCOxAK2haQ== +"@changesets/read@^0.6.0": + version "0.6.0" + resolved "https://registry.yarnpkg.com/@changesets/read/-/read-0.6.0.tgz#27e13b58d0b0eb3b0a5cba48a3f4f71f05ef4610" + integrity sha512-ZypqX8+/im1Fm98K4YcZtmLKgjs1kDQ5zHpc2U1qdtNBmZZfo/IBiG162RoP0CUF05tvp2y4IspH11PLnPxuuw== dependencies: "@babel/runtime" "^7.20.1" - "@changesets/git" "^2.0.0" - "@changesets/logger" "^0.0.5" - "@changesets/parse" "^0.3.16" - "@changesets/types" "^5.2.1" + "@changesets/git" "^3.0.0" + "@changesets/logger" "^0.1.0" + "@changesets/parse" "^0.4.0" + "@changesets/types" "^6.0.0" chalk "^2.1.0" fs-extra "^7.0.1" p-filter "^2.1.0" @@ -1822,18 +1814,18 @@ resolved "https://registry.yarnpkg.com/@changesets/types/-/types-4.1.0.tgz#fb8f7ca2324fd54954824e864f9a61a82cb78fe0" integrity sha512-LDQvVDv5Kb50ny2s25Fhm3d9QSZimsoUGBsUioj6MC3qbMUCuC8GPIvk/M6IvXx3lYhAs0lwWUQLb+VIEUCECw== -"@changesets/types@^5.2.1": - version "5.2.1" - resolved "https://registry.yarnpkg.com/@changesets/types/-/types-5.2.1.tgz#a228c48004aa8a93bce4be2d1d31527ef3bf21f6" - integrity sha512-myLfHbVOqaq9UtUKqR/nZA/OY7xFjQMdfgfqeZIBK4d0hA6pgxArvdv8M+6NUzzBsjWLOtvApv8YHr4qM+Kpfg== +"@changesets/types@^6.0.0": + version "6.0.0" + resolved "https://registry.yarnpkg.com/@changesets/types/-/types-6.0.0.tgz#e46abda9890610dd1fbe1617730173d2267544bd" + integrity sha512-b1UkfNulgKoWfqyHtzKS5fOZYSJO+77adgL7DLRDr+/7jhChN+QcHnbjiQVOz/U+Ts3PGNySq7diAItzDgugfQ== -"@changesets/write@^0.2.3": - version "0.2.3" - resolved "https://registry.yarnpkg.com/@changesets/write/-/write-0.2.3.tgz#baf6be8ada2a67b9aba608e251bfea4fdc40bc63" - integrity sha512-Dbamr7AIMvslKnNYsLFafaVORx4H0pvCA2MHqgtNCySMe1blImEyAEOzDmcgKAkgz4+uwoLz7demIrX+JBr/Xw== +"@changesets/write@^0.3.0": + version "0.3.0" + resolved "https://registry.yarnpkg.com/@changesets/write/-/write-0.3.0.tgz#c6c5bc390cce4031da20eab8a4ca2d71453a1985" + integrity sha512-slGLb21fxZVUYbyea+94uFiD6ntQW0M2hIKNznFizDhZPDgn2c/fv1UzzlW43RVzh1BEDuIqW6hzlJ1OflNmcw== dependencies: "@babel/runtime" "^7.20.1" - "@changesets/types" "^5.2.1" + "@changesets/types" "^6.0.0" fs-extra "^7.0.1" human-id "^1.0.2" prettier "^2.7.1" @@ -2189,10 +2181,10 @@ dependencies: "@sinclair/typebox" "^0.25.16" -"@jest/schemas@^29.6.0": - version "29.6.0" - resolved "https://registry.yarnpkg.com/@jest/schemas/-/schemas-29.6.0.tgz#0f4cb2c8e3dca80c135507ba5635a4fd755b0040" - integrity sha512-rxLjXyJBTL4LQeJW3aKo0M/+GkCOXsO+8i9Iu7eDb6KwtP65ayoDsitrdPBtujxQ88k4wI2FNYfa6TOGwSn6cQ== +"@jest/schemas@^29.6.3": + version "29.6.3" + resolved "https://registry.yarnpkg.com/@jest/schemas/-/schemas-29.6.3.tgz#430b5ce8a4e0044a7e3819663305a7b3091c8e03" + integrity sha512-mo5j5X+jIZmJQveBKeS/clAueipV7KgiX1vMgCxam1RNYiqE1w62n0/tJJnHtjW8ZHcQco5gY85jA3mi0L+nSA== dependencies: "@sinclair/typebox" "^0.27.8" @@ -2352,12 +2344,12 @@ "@types/yargs" "^17.0.8" chalk "^4.0.0" -"@jest/types@^29.6.1": - version "29.6.1" - resolved "https://registry.yarnpkg.com/@jest/types/-/types-29.6.1.tgz#ae79080278acff0a6af5eb49d063385aaa897bf2" - integrity sha512-tPKQNMPuXgvdOn2/Lg9HNfUvjYVGolt04Hp03f5hAk878uwOLikN+JzeLY0HcVgKgFl9Hs3EIqpu3WX27XNhnw== +"@jest/types@^29.6.3": + version "29.6.3" + resolved "https://registry.yarnpkg.com/@jest/types/-/types-29.6.3.tgz#1131f8cf634e7e84c5e77bab12f052af585fba59" + integrity sha512-u3UPsIilWKOM3F9CXtrG8LEJmNxwoCQC/XVj4IKYXvvpx7QIi/Kg1LI5uDmDpKlac62NUtX7eLjRh+jVZcLOzw== dependencies: - "@jest/schemas" "^29.6.0" + "@jest/schemas" "^29.6.3" "@types/istanbul-lib-coverage" "^2.0.0" "@types/istanbul-reports" "^3.0.0" "@types/node" "*" @@ -2660,22 +2652,46 @@ resolved "https://registry.yarnpkg.com/@pkgjs/parseargs/-/parseargs-0.11.0.tgz#a77ea742fab25775145434eb1d2328cf5013ac33" integrity sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg== -"@prisma/client@^4.13.0": - version "4.13.0" - resolved "https://registry.yarnpkg.com/@prisma/client/-/client-4.13.0.tgz#271d2b9756503ea17bbdb459c7995536cf2a6191" - integrity sha512-YaiiICcRB2hatxsbnfB66uWXjcRw3jsZdlAVxmx0cFcTc/Ad/sKdHCcWSnqyDX47vAewkjRFwiLwrOUjswVvmA== +"@prisma/client@^5.8.1": + version "5.8.1" + resolved "https://registry.yarnpkg.com/@prisma/client/-/client-5.8.1.tgz#7815ec51c0ca2a6de219c02e7846701ae3baf240" + integrity sha512-xQtMPfbIwLlbm0VVIVQY2yqQVOxPwRQhvIp7Z3m2900g1bu/zRHKhYZJQWELqmjl6d8YwBy0K2NvMqh47v1ubw== + +"@prisma/debug@5.8.1": + version "5.8.1" + resolved "https://registry.yarnpkg.com/@prisma/debug/-/debug-5.8.1.tgz#704daa36919b0fc4d227260ecebfa1c94b155b07" + integrity sha512-tjuw7eA0Us3T42jx9AmAgL58rzwzpFGYc3R7Y4Ip75EBYrKMBA1YihuWMcBC92ILmjlQ/u3p8VxcIE0hr+fZfg== + +"@prisma/engines-version@5.8.1-1.78caf6feeaed953168c64e15a249c3e9a033ebe2": + version "5.8.1-1.78caf6feeaed953168c64e15a249c3e9a033ebe2" + resolved "https://registry.yarnpkg.com/@prisma/engines-version/-/engines-version-5.8.1-1.78caf6feeaed953168c64e15a249c3e9a033ebe2.tgz#f600a45afc4cf0c0356b6ed90add6050fa3f3239" + integrity sha512-f5C3JM3l9yhGr3cr4FMqWloFaSCpNpMi58Om22rjD2DOz3owci2mFdFXMgnAGazFPKrCbbEhcxdsRfspEYRoFQ== + +"@prisma/engines@5.8.1": + version "5.8.1" + resolved "https://registry.yarnpkg.com/@prisma/engines/-/engines-5.8.1.tgz#b850751f5bf7d5e570b9fe16cefdc2b1fd2c02c3" + integrity sha512-TJgYLRrZr56uhqcXO4GmP5be+zjCIHtLDK20Cnfg+o9d905hsN065QOL+3Z0zQAy6YD31Ol4u2kzSfRmbJv/uA== dependencies: - "@prisma/engines-version" "4.13.0-50.1e7af066ee9cb95cf3a403c78d9aab3e6b04f37a" + "@prisma/debug" "5.8.1" + "@prisma/engines-version" "5.8.1-1.78caf6feeaed953168c64e15a249c3e9a033ebe2" + "@prisma/fetch-engine" "5.8.1" + "@prisma/get-platform" "5.8.1" -"@prisma/engines-version@4.13.0-50.1e7af066ee9cb95cf3a403c78d9aab3e6b04f37a": - version "4.13.0-50.1e7af066ee9cb95cf3a403c78d9aab3e6b04f37a" - resolved "https://registry.yarnpkg.com/@prisma/engines-version/-/engines-version-4.13.0-50.1e7af066ee9cb95cf3a403c78d9aab3e6b04f37a.tgz#ae338908d11685dee50e7683502d75442b955bf9" - integrity sha512-fsQlbkhPJf08JOzKoyoD9atdUijuGBekwoOPZC3YOygXEml1MTtgXVpnUNchQlRSY82OQ6pSGQ9PxUe4arcSLQ== +"@prisma/fetch-engine@5.8.1": + version "5.8.1" + resolved "https://registry.yarnpkg.com/@prisma/fetch-engine/-/fetch-engine-5.8.1.tgz#38bb92f1fbd3669340a3cc49fce403ab4df671dd" + integrity sha512-+bgjjoSFa6uYEbAPlklfoVSStOEfcpheOjoBoNsNNSQdSzcwE2nM4Q0prun0+P8/0sCHo18JZ9xqa8gObvgOUw== + dependencies: + "@prisma/debug" "5.8.1" + "@prisma/engines-version" "5.8.1-1.78caf6feeaed953168c64e15a249c3e9a033ebe2" + "@prisma/get-platform" "5.8.1" -"@prisma/engines@4.13.0": - version "4.13.0" - resolved "https://registry.yarnpkg.com/@prisma/engines/-/engines-4.13.0.tgz#582a6b90b6efeb0f465984f1fe0e72a4afaaa5ae" - integrity sha512-HrniowHRZXHuGT9XRgoXEaP2gJLXM5RMoItaY2PkjvuZ+iHc0Zjbm/302MB8YsPdWozAPHHn+jpFEcEn71OgPw== +"@prisma/get-platform@5.8.1": + version "5.8.1" + resolved "https://registry.yarnpkg.com/@prisma/get-platform/-/get-platform-5.8.1.tgz#8cd450b65a52a5a6ed5b2f52457136a492c0f251" + integrity sha512-wnA+6HTFcY+tkykMokix9GiAkaauPC5W/gg0O5JB0J8tCTNWrqpnQ7AsaGRfkYUbeOIioh6woDjQrGTTRf1Zag== + dependencies: + "@prisma/debug" "5.8.1" "@redis/bloom@1.2.0": version "1.2.0" @@ -2787,6 +2803,13 @@ dependencies: "@shopify/graphql-client" "^0.9.1" +"@shopify/admin-api-client@^0.2.2": + version "0.2.2" + resolved "https://registry.yarnpkg.com/@shopify/admin-api-client/-/admin-api-client-0.2.2.tgz#c288b4af8834bc7ab6c7c1ad26e7feddfa648ec6" + integrity sha512-7x79Ras5gbf7U3oVBjFIPF70WUUit3kLF93IMhtTaC9OJ/b9NIeW1w4/+RSJq3FM/7MGQGUkyrrnuNI3nl3Eig== + dependencies: + "@shopify/graphql-client" "^0.9.2" + "@shopify/babel-preset@^24.1.4": version "24.1.5" resolved "https://registry.yarnpkg.com/@shopify/babel-preset/-/babel-preset-24.1.5.tgz#76cfef62bb8a4d9769559f3f2505148a55d13488" @@ -2839,20 +2862,25 @@ pkg-dir "^5.0.0" pluralize "^8.0.0" -"@shopify/generate-docs@^0.11.1": - version "0.11.1" - resolved "https://registry.yarnpkg.com/@shopify/generate-docs/-/generate-docs-0.11.1.tgz#4b4d159cb5578dc98fa4218bbc9edade1872d0f1" - integrity sha512-pZxCkUtO6MXJaetEdd4Tyk6t7tjJTUNd3VGCXyOeZqKAWrXEob+8gkost0x23EmuES4GGqQKSB9nnKdFvbdk1w== +"@shopify/generate-docs@^0.13.1": + version "0.13.1" + resolved "https://registry.yarnpkg.com/@shopify/generate-docs/-/generate-docs-0.13.1.tgz#c2f41671c488a70e4bf91648ffc5631c56febc3e" + integrity sha512-V3vk6oemlqX/1joFYLPjvzephgVTzbuysw9lig7UgSbuMah0kXq+VSvF9IxgQMnfloTT32nWbkBtl+AJTQurtw== dependencies: "@types/react" "^18.0.21" globby "^11.1.0" - typescript "^4.8.3" + typescript "^4.8.3 || ^5.0.0" "@shopify/graphql-client@^0.9.1": version "0.9.1" resolved "https://registry.yarnpkg.com/@shopify/graphql-client/-/graphql-client-0.9.1.tgz#0c37a4143c0dd26837e82b8fd13e1b64fabf90dc" integrity sha512-yx6PIHY8u4O0ijUQccmIpjoyM1JSbe5RkrSyXSbjhsS+nla0WAJDIMw4R01mjx3dFnVIcGONhRX/QrUq97uWig== +"@shopify/graphql-client@^0.9.2": + version "0.9.2" + resolved "https://registry.yarnpkg.com/@shopify/graphql-client/-/graphql-client-0.9.2.tgz#2c37ba20bbc1713c78b1b241af99405cd92ab428" + integrity sha512-Xk7bPA3UsLXFHV6F39t5g8dTVxqPagwJoR+MLYUvCuhlWpDeZW3f/OcgShXPPben5NcnOVV38kj5GVlkvrWqiQ== + "@shopify/loom-cli@^1.1.0": version "1.1.0" resolved "https://registry.yarnpkg.com/@shopify/loom-cli/-/loom-cli-1.1.0.tgz#e92f423a26f00c33b7c247a65667a8ff9bd1e8f8" @@ -2990,14 +3018,14 @@ jest-matcher-utils "^26.6.2" react-reconciler "^0.28.0" -"@shopify/shopify-api@^9.0.1": - version "9.0.1" - resolved "https://registry.yarnpkg.com/@shopify/shopify-api/-/shopify-api-9.0.1.tgz#7f236d94d902b3671c6a11f11f3561f98ddc7422" - integrity sha512-ToqT5zt/YA+VbFcLAoEVr6grs6v+Y9io5kuULy9Zhm5RClFEhkpRLM4U+vrrV5XjWYC6pcxs00WjpSi0onryrw== +"@shopify/shopify-api@^9.0.2": + version "9.0.2" + resolved "https://registry.yarnpkg.com/@shopify/shopify-api/-/shopify-api-9.0.2.tgz#01a321fc6bcf3450572751e6c1f8c1e4ba8ab78a" + integrity sha512-ZOnzDein63JMatQAYshg2RyBXcseBUTP8stbrwG/FwDhWqNafxMC2O8AcoIGoaNc1SoRYbWpxCbwkeO/CrW7Vg== dependencies: - "@shopify/admin-api-client" "^0.2.1" + "@shopify/admin-api-client" "^0.2.2" "@shopify/network" "^3.2.1" - "@shopify/storefront-api-client" "^0.2.1" + "@shopify/storefront-api-client" "^0.2.2" compare-versions "^5.0.3" isbot "^3.6.10" jose "^4.9.1" @@ -3012,6 +3040,13 @@ dependencies: "@shopify/graphql-client" "^0.9.1" +"@shopify/storefront-api-client@^0.2.2": + version "0.2.2" + resolved "https://registry.yarnpkg.com/@shopify/storefront-api-client/-/storefront-api-client-0.2.2.tgz#79ddddd70838617e7a1a509f2d58ce148457e293" + integrity sha512-sPpbXEGYbCRQ1jvwdEMtrutUTLmwtrZ30nHBLuKLIgtezFmhuvF5/FD/hgPQCxWxEukZt8518gAVgE6p+9D8ig== + dependencies: + "@shopify/graphql-client" "^0.9.2" + "@shopify/typescript-configs@^5.1.0": version "5.1.0" resolved "https://registry.yarnpkg.com/@shopify/typescript-configs/-/typescript-configs-5.1.0.tgz#f6e8fdd3291bf0a406578b2c6eb21f8c542d3c0a" @@ -3566,13 +3601,6 @@ dependencies: "@types/node" "*" -"@types/is-ci@^3.0.0": - version "3.0.0" - resolved "https://registry.yarnpkg.com/@types/is-ci/-/is-ci-3.0.0.tgz#7e8910af6857601315592436f030aaa3ed9783c3" - integrity sha512-Q0Op0hdWbYd1iahB+IFNQcWXFq4O0Q5MwQP7uN0souuQ4rPg1vEYcnIOfr1gY+M+6rc8FGoRaBO1mOOvL29sEQ== - dependencies: - ci-info "^3.1.0" - "@types/istanbul-lib-coverage@*", "@types/istanbul-lib-coverage@^2.0.0", "@types/istanbul-lib-coverage@^2.0.1": version "2.0.4" resolved "https://registry.yarnpkg.com/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.4.tgz#8467d4b3c087805d63580480890791277ce35c44" @@ -3718,9 +3746,9 @@ csstype "^3.0.2" "@types/react@^18.0.21": - version "18.2.21" - resolved "https://registry.yarnpkg.com/@types/react/-/react-18.2.21.tgz#774c37fd01b522d0b91aed04811b58e4e0514ed9" - integrity sha512-neFKG/sBAwGxHgXiIxnbm3/AAVQ/cMRS93hvBpg8xYRbeQSPVABp9U2bRnPf0iI4+Ucdv3plSxKK+3CW2ENJxA== + version "18.2.48" + resolved "https://registry.yarnpkg.com/@types/react/-/react-18.2.48.tgz#11df5664642d0bd879c1f58bc1d37205b064e8f1" + integrity sha512-qboRCl6Ie70DQQG9hhNREz81jqC1cs9EVNcjQ1AU+jH6NFfSAhVVbrrY/+nSF+Bsk4AOwm9Qa61InvMCyV+H3w== dependencies: "@types/prop-types" "*" "@types/scheduler" "*" @@ -3747,12 +3775,7 @@ resolved "https://registry.yarnpkg.com/@types/scheduler/-/scheduler-0.16.3.tgz#cef09e3ec9af1d63d2a6cc5b383a737e24e6dcf5" integrity sha512-5cJ8CB4yAx7BH1oMvdU0Jh9lrEXyPkar6F9G/ERswkCuvP4KQZfZkSjcMbAICCpQTN4OuZn8tz0HiKv9TGZgrQ== -"@types/semver@^6.0.0": - version "6.2.3" - resolved "https://registry.yarnpkg.com/@types/semver/-/semver-6.2.3.tgz#5798ecf1bec94eaa64db39ee52808ec0693315aa" - integrity sha512-KQf+QAMWKMrtBMsB8/24w53tEsxllMj6TuA80TT/5igJalLI/zm0L3oXRbIAl4Ohfc85gyHX/jhMwsVkmhLU4A== - -"@types/semver@^7.3.12", "@types/semver@^7.5.6": +"@types/semver@^7.3.12", "@types/semver@^7.5.0", "@types/semver@^7.5.6": version "7.5.6" resolved "https://registry.yarnpkg.com/@types/semver/-/semver-7.5.6.tgz#c65b2bfce1bec346582c07724e3f8c1017a20339" integrity sha512-dn1l8LaMea/IjDoHNd9J52uBbInB796CDffS6VdIxvqYCPSG0V0DzHp76GpaWnlhg88uYyPbXCDIowa86ybd5A== @@ -4611,10 +4634,10 @@ chownr@^2.0.0: resolved "https://registry.yarnpkg.com/chownr/-/chownr-2.0.0.tgz#15bfbe53d2eab4cf70f18a8cd68ebe5b3cb1dece" integrity sha512-bIomtDF5KGpdogkLd9VspvFzk9KfpyyGlS8YFVZl7TGPBHL5snIOnxeshwVgPteQ9b4Eydl+pVbIyE1DcvCWgQ== -ci-info@^3.1.0, ci-info@^3.2.0: - version "3.8.0" - resolved "https://registry.yarnpkg.com/ci-info/-/ci-info-3.8.0.tgz#81408265a5380c929f0bc665d62256628ce9ef91" - integrity sha512-eXTggHWSooYhq49F2opQhuHWgzucfF2YgODK4e1566GQs5BIfP30B0oenwBJHfWxAs2fyPB1s7Mg949zLf61Yw== +ci-info@^3.2.0, ci-info@^3.7.0: + version "3.9.0" + resolved "https://registry.yarnpkg.com/ci-info/-/ci-info-3.9.0.tgz#4279a62028a7b1f262f3473fc9605f5e218c59b4" + integrity sha512-NIxF55hv4nSqQswkAeiOi1r83xy8JldOFDTWiug55KBu9Jnblncd2U6ViHmYgHf01TPZS77NJBhBMKdWj9HQMQ== cjs-module-lexer@^1.0.0: version "1.2.2" @@ -6490,13 +6513,6 @@ is-callable@^1.1.3, is-callable@^1.1.4, is-callable@^1.2.7: resolved "https://registry.yarnpkg.com/is-callable/-/is-callable-1.2.7.tgz#3bc2a85ea742d9e36205dcacdd72ca1fdc51b055" integrity sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA== -is-ci@^3.0.1: - version "3.0.1" - resolved "https://registry.yarnpkg.com/is-ci/-/is-ci-3.0.1.tgz#db6ecbed1bd659c43dac0f45661e7674103d1867" - integrity sha512-ZYvCgrefwqoQ6yTyYUbQu64HsITZ3NfKX1lzaEYdkTDcfKzzCI/wthRRYKkdjHKFVgNiXKAKm65Zo1pk2as/QQ== - dependencies: - ci-info "^3.2.0" - is-core-module@^2.11.0, is-core-module@^2.9.0: version "2.12.0" resolved "https://registry.yarnpkg.com/is-core-module/-/is-core-module-2.12.0.tgz#36ad62f6f73c8253fd6472517a12483cf03e7ec4" @@ -7629,7 +7645,7 @@ jest@^27.2.4: import-local "^3.0.2" jest-cli "^27.5.1" -jest@^29.1.0, jest@^29.5.0: +jest@^29.1.0: version "29.5.0" resolved "https://registry.yarnpkg.com/jest/-/jest-29.5.0.tgz#f75157622f5ce7ad53028f2f8888ab53e1f1f24e" integrity sha512-juMg3he2uru1QoXX078zTa7pO85QyB9xajZc6bU+d9yEGwrKX6+vGmJQ3UdVZsvTEUARIdObzH68QItim6OSSQ== @@ -8939,7 +8955,7 @@ prettier-linter-helpers@^1.0.0: dependencies: fast-diff "^1.1.2" -prettier@^2.7.1, prettier@^2.8.7, prettier@^2.8.8: +prettier@^2.7.1, prettier@^2.8.8: version "2.8.8" resolved "https://registry.yarnpkg.com/prettier/-/prettier-2.8.8.tgz#e8c5d7e98a4305ffe3de2e1fc4aca1a71c28b1da" integrity sha512-tdN8qQGvNjw4CHbY+XXk0JgCXn9QiF21a55rBe5LJAU+kDyC4WQn4+awm2Xfk2lQMk5fKup9XgzTZtGkjBdP9Q== @@ -8982,12 +8998,12 @@ pretty-format@^29.0.0, pretty-format@^29.5.0: ansi-styles "^5.0.0" react-is "^18.0.0" -prisma@^4.13.0: - version "4.13.0" - resolved "https://registry.yarnpkg.com/prisma/-/prisma-4.13.0.tgz#0b83f40acf50cd47d7463a135c4e9b275713e602" - integrity sha512-L9mqjnSmvWIRCYJ9mQkwCtj4+JDYYTdhoyo8hlsHNDXaZLh/b4hR0IoKIBbTKxZuyHQzLopb/+0Rvb69uGV7uA== +prisma@^5.8.1: + version "5.8.1" + resolved "https://registry.yarnpkg.com/prisma/-/prisma-5.8.1.tgz#1f101793a8831c0719dfbed5f85a96ea4888c9d3" + integrity sha512-N6CpjzECnUHZ5beeYpDzkt2rYpEdAeqXX2dweu6BoQaeYkNZrC/WJHM+5MO/uidFHTak8QhkPKBWck1o/4MD4A== dependencies: - "@prisma/engines" "4.13.0" + "@prisma/engines" "5.8.1" promise-inflight@^1.0.1: version "1.0.1" @@ -9449,12 +9465,12 @@ semiver@^1.1.0: resolved "https://registry.yarnpkg.com/semiver/-/semiver-1.1.0.tgz#9c97fb02c21c7ce4fcf1b73e2c7a24324bdddd5f" integrity sha512-QNI2ChmuioGC1/xjyYwyZYADILWyW6AmS1UH6gDj/SFUUUS4MBAWs/7mxnkRPc/F4iHezDP+O8t0dO8WHiEOdg== -"semver@2 || 3 || 4 || 5", semver@^5.4.1, semver@^5.6.0: +"semver@2 || 3 || 4 || 5", semver@^5.6.0: version "5.7.1" resolved "https://registry.yarnpkg.com/semver/-/semver-5.7.1.tgz#a954f931aeba508d307bbf069eff0c01c96116f7" integrity sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ== -semver@7.x, semver@^7.0.0, semver@^7.3.2, semver@^7.3.5, semver@^7.3.7, semver@^7.3.8, semver@^7.5.4: +semver@7.x, semver@^7.0.0, semver@^7.3.2, semver@^7.3.5, semver@^7.3.7, semver@^7.3.8, semver@^7.5.3, semver@^7.5.4: version "7.5.4" resolved "https://registry.yarnpkg.com/semver/-/semver-7.5.4.tgz#483986ec4ed38e1c6c48c34894a9182dbff68a6e" integrity sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA== @@ -10197,11 +10213,16 @@ typedarray-to-buffer@^3.1.5: dependencies: is-typedarray "^1.0.0" -typescript@4.9.5, typescript@^4.3.5, typescript@^4.8.3, typescript@^4.9.5: +typescript@^4.3.5, typescript@^4.9.5: version "4.9.5" resolved "https://registry.yarnpkg.com/typescript/-/typescript-4.9.5.tgz#095979f9bcc0d09da324d58d03ce8f8374cbe65a" integrity sha512-1FXk9E2Hm+QzZQ7z+McJiHL4NW1F2EzMu9Nq9i3zAaGqibafqYwCVU6WyWAuyQRRzOlxou8xZSyXLEN8oKj24g== +"typescript@^4.8.3 || ^5.0.0": + version "5.3.3" + resolved "https://registry.yarnpkg.com/typescript/-/typescript-5.3.3.tgz#b3ce6ba258e72e6305ba66f5c9b452aaee3ffe37" + integrity sha512-pXWcraxM0uxAS+tN0AG/BF2TyqmHO014Z070UsJ+pFvYuRSq8KH8DmWpnbXe0pEPDHXZV3FcAbJkijJ5oNEnWw== + unbox-primitive@^1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/unbox-primitive/-/unbox-primitive-1.0.2.tgz#29032021057d5e6cdbd08c5129c226dff8ed6f9e"