diff --git a/.changeset/fresh-bottles-tell.md b/.changeset/fresh-bottles-tell.md new file mode 100644 index 0000000000..a845151cc8 --- /dev/null +++ b/.changeset/fresh-bottles-tell.md @@ -0,0 +1,2 @@ +--- +--- diff --git a/.changeset/friendly-laws-yell.md b/.changeset/friendly-laws-yell.md new file mode 100644 index 0000000000..ed945955f5 --- /dev/null +++ b/.changeset/friendly-laws-yell.md @@ -0,0 +1,5 @@ +--- +'@shopify/shopify-app-session-storage-redis': patch +--- + +Updates redis from 4.6.11 to 4.6.12. diff --git a/.changeset/lucky-cooks-glow.md b/.changeset/lucky-cooks-glow.md new file mode 100644 index 0000000000..a845151cc8 --- /dev/null +++ b/.changeset/lucky-cooks-glow.md @@ -0,0 +1,2 @@ +--- +--- diff --git a/.changeset/proud-knives-happen.md b/.changeset/proud-knives-happen.md new file mode 100644 index 0000000000..a845151cc8 --- /dev/null +++ b/.changeset/proud-knives-happen.md @@ -0,0 +1,2 @@ +--- +--- diff --git a/.github/workflows/stale.yml b/.github/workflows/stale.yml index 6400f550ca..d230026a65 100644 --- a/.github/workflows/stale.yml +++ b/.github/workflows/stale.yml @@ -13,19 +13,13 @@ jobs: - uses: actions/stale@v5 with: days-before-issue-stale: 60 - days-before-issue-close: 14 operations-per-run: 1000 stale-issue-label: "Stale" - stale-issue-message: > - This issue is stale because it has been open for 90 days with no activity. It will be closed if no further action occurs in 14 days. - close-issue-message: | - We are closing this issue because it has been inactive for a few months. - This probably means that it is not reproducible or it has been fixed in a newer version. - If it's an enhancement and hasn't been taken on since it was submitted, then it seems other issues have taken priority. + stale-issue-message: | + We're labeling this issue as stale because there hasn't been any activity on it for 60 days. While the issue will stay open and we hope to resolve it, this helps us prioritize community requests. - If you still encounter this issue with the latest stable version, please reopen using the issue template. You can also contribute directly by submitting a pull request– see the [CONTRIBUTING.md](https://github.com/Shopify/shopify-app-js/blob/main/CONTRIBUTING.md) file for guidelines - - Thank you! + You can add a comment to remove the label if it's still relevant, and we can re-evaluate it. + days-before-issue-close: -1 days-before-pr-stale: -1 days-before-pr-close: -1 repo-token: ${{ secrets.GITHUB_TOKEN }} diff --git a/packages/shopify-app-remix/CHANGELOG.md b/packages/shopify-app-remix/CHANGELOG.md index 51dd03c34e..33243d2eb7 100644 --- a/packages/shopify-app-remix/CHANGELOG.md +++ b/packages/shopify-app-remix/CHANGELOG.md @@ -6,6 +6,8 @@ - 2473c85: Add new embedded authorization strategy relying on Shopify managed install and OAuth token exchange + :exclamation: For more information on how to enable this feature, see ["New Embedded Authorization Strategy"](./README.md#new-embedded-authorization-strategy) + ### Patch Changes - 35b74dd: Fixes a bug that was causing external redirects to fail in remix actions diff --git a/packages/shopify-app-remix/README.md b/packages/shopify-app-remix/README.md index 6f0ae55fa7..9c1e412381 100644 --- a/packages/shopify-app-remix/README.md +++ b/packages/shopify-app-remix/README.md @@ -174,6 +174,50 @@ Here are some guides to help you set up your app: You can also authenticate requests from surfaces other than the admin. To see all supported methods, see [the `shopify.authenticate` object documentation](https://shopify.dev/docs/api/shopify-app-remix/latest/authenticate). +### New embedded app authorization strategy + +> [!TIP] +> If you are building an embedded app, we **strongly** recommend using [Shopify managed installation](https://shopify.dev/docs/apps/auth/installation#shopify-managed-installation) +> with [token exchange](https://shopify.dev/docs/apps/auth/get-access-tokens/token-exchange) instead of the legacy authorization code grant flow. + +We've introduced a new installation and authorization strategy for **embedded apps** that +eliminates the redirects that were previously necessary. +It replaces the existing [installation and authorization code grant flow](https://shopify.dev/docs/apps/auth/get-access-tokens/authorization-code-grant). + +This is achieved by using [Shopify managed installation](https://shopify.dev/docs/apps/auth/installation#shopify-managed-installation) +to handle automatic app installations and scope updates, while utilizing +[token exchange](https://shopify.dev/docs/apps/auth/get-access-tokens/token-exchange) to retrieve an access token for +authenticated API access. + +##### Enabling this new strategy in your app + +> [!NOTE] +> Newly created Remix apps from the template after February 1st 2024 has this feature enabled by default. + +1. Enable [Shopify managed installation](https://shopify.dev/docs/apps/auth/installation#shopify-managed-installation) + by configuring your scopes [through the Shopify CLI](https://shopify.dev/docs/apps/tools/cli/configuration). +2. Enable the future flag `unstable_newEmbeddedAuthStrategy` in your app's server configuration file. + +```ts +// my-app/app/shopify.server.ts +const shopify = shopifyApp({ + ... + isEmbeddedApp: true, + future: { + unstable_newEmbeddedAuthStrategy: true, + } +}) + +``` + +3. Enjoy a smoother and faster app installation process. + +###### Learn more about: + +- [How token exchange works](https://shopify.dev/docs/apps/auth/get-access-tokens/token-exchange) +- [Using Shopify managed installation](https://shopify.dev/docs/apps/auth/installation#shopify-managed-installation) +- [Configuring access scopes through the Shopify CLI](https://shopify.dev/docs/apps/tools/cli/configuration) + ## Gotchas / Troubleshooting If you're experiencing unexpected behaviors when using this package, check our [app template's documentation](https://github.com/Shopify/shopify-app-template-remix#gotchas--troubleshooting) for the solution to common issues. 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 7d94cf2021..230f85d2f3 100644 --- a/packages/shopify-app-remix/docs/generated/generated_docs_data.json +++ b/packages/shopify-app-remix/docs/generated/generated_docs_data.json @@ -780,7 +780,7 @@ "filePath": "src/server/clients/types.ts", "syntaxKind": "PropertySignature", "name": "headers", - "value": "{ [key: string]: any; }", + "value": "Record", "description": "", "isOptional": true }, @@ -793,7 +793,7 @@ "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}" + "value": "export interface GraphQLQueryOptions<\n Operation extends keyof Operations,\n Operations extends AllOperations,\n> {\n variables?: ApiClientRequestOptions['variables'];\n apiVersion?: ApiVersion;\n headers?: Record;\n tries?: number;\n}" }, "ApiVersion": { "filePath": "../../node_modules/@shopify/shopify-api/lib/types.d.ts", @@ -2782,7 +2782,7 @@ "filePath": "src/server/clients/types.ts", "syntaxKind": "PropertySignature", "name": "headers", - "value": "{ [key: string]: any; }", + "value": "Record", "description": "", "isOptional": true }, @@ -2795,7 +2795,7 @@ "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}" + "value": "export interface GraphQLQueryOptions<\n Operation extends keyof Operations,\n Operations extends AllOperations,\n> {\n variables?: ApiClientRequestOptions['variables'];\n apiVersion?: ApiVersion;\n headers?: Record;\n tries?: number;\n}" }, "ApiVersion": { "filePath": "../../node_modules/@shopify/shopify-api/lib/types.d.ts", @@ -3600,14 +3600,14 @@ { "filePath": "src/server/types.ts", "syntaxKind": "MethodSignature", - "name": "__@iterator@1657", + "name": "__@iterator@1695", "value": "() => IterableIterator", "description": "Iterator" }, { "filePath": "src/server/types.ts", "syntaxKind": "PropertySignature", - "name": "__@unscopables@1659", + "name": "__@unscopables@1697", "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." }, @@ -4003,7 +4003,7 @@ "filePath": "src/server/authenticate/webhooks/types.ts", "syntaxKind": "TypeAliasDeclaration", "name": "WebhookAdminContext", - "value": "FeatureEnabled extends true\n ? AdminApiContext\n : LegacyWebhookAdminApiContext", + "value": "FeatureEnabled extends true\n ? AdminApiContext\n : LegacyWebhookAdminApiContext", "description": "" }, "AdminContext": { @@ -4364,7 +4364,7 @@ "filePath": "src/server/clients/types.ts", "syntaxKind": "PropertySignature", "name": "headers", - "value": "{ [key: string]: any; }", + "value": "Record", "description": "", "isOptional": true }, @@ -4377,7 +4377,7 @@ "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}" + "value": "export interface GraphQLQueryOptions<\n Operation extends keyof Operations,\n Operations extends AllOperations,\n> {\n variables?: ApiClientRequestOptions['variables'];\n apiVersion?: ApiVersion;\n headers?: Record;\n tries?: number;\n}" }, "ApiVersion": { "filePath": "../../node_modules/@shopify/shopify-api/lib/types.d.ts", @@ -8272,7 +8272,7 @@ "filePath": "src/server/clients/types.ts", "syntaxKind": "PropertySignature", "name": "headers", - "value": "{ [key: string]: any; }", + "value": "Record", "description": "", "isOptional": true }, @@ -8285,7 +8285,7 @@ "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}" + "value": "export interface GraphQLQueryOptions<\n Operation extends keyof Operations,\n Operations extends AllOperations,\n> {\n variables?: ApiClientRequestOptions['variables'];\n apiVersion?: ApiVersion;\n headers?: Record;\n tries?: number;\n}" }, "ApiVersion": { "filePath": "../../node_modules/@shopify/shopify-api/lib/types.d.ts", @@ -8522,7 +8522,7 @@ "filePath": "src/server/clients/types.ts", "syntaxKind": "PropertySignature", "name": "headers", - "value": "{ [key: string]: any; }", + "value": "Record", "description": "", "isOptional": true }, @@ -8535,7 +8535,7 @@ "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}" + "value": "export interface GraphQLQueryOptions<\n Operation extends keyof Operations,\n Operations extends AllOperations,\n> {\n variables?: ApiClientRequestOptions['variables'];\n apiVersion?: ApiVersion;\n headers?: Record;\n tries?: number;\n}" }, "ApiVersion": { "filePath": "../../node_modules/@shopify/shopify-api/lib/types.d.ts", @@ -8652,7 +8652,7 @@ "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}", + "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 authStrategy = authStrategyFactory({\n ...params,\n strategy:\n config.future.unstable_newEmbeddedAuthStrategy && config.isEmbeddedApp\n ? new TokenExchangeStrategy(params)\n : new AuthCodeFlowStrategy(params),\n });\n\n const shopify:\n | AdminApp\n | AppStoreApp\n | SingleMerchantApp = {\n sessionStorage: config.sessionStorage,\n addDocumentResponseHeaders: addDocumentResponseHeadersFactory(params),\n registerWebhooks: registerWebhooksFactory(params),\n authenticate: {\n admin: authStrategy,\n 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", @@ -8897,456 +8897,178 @@ }, "WebhookConfig": { "filePath": "src/server/config-types.ts", + "syntaxKind": "TypeAliasDeclaration", "name": "WebhookConfig", + "value": "Record", + "description": "", + "members": [] + }, + "HooksConfig": { + "filePath": "src/server/config-types.ts", + "name": "HooksConfig", "description": "", "members": [ { "filePath": "src/server/config-types.ts", - "name": "[key: string]", - "value": "WebhookHandler | WebhookHandler[]" + "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": "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": "" + "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}" }, - "HttpWebhookHandler": { - "filePath": "../../node_modules/@shopify/shopify-api/lib/webhooks/types.d.ts", - "name": "HttpWebhookHandler", + "AfterAuthOptions": { + "filePath": "src/server/config-types.ts", + "name": "AfterAuthOptions", "description": "", "members": [ { - "filePath": "../../node_modules/@shopify/shopify-api/lib/webhooks/types.d.ts", + "filePath": "src/server/config-types.ts", "syntaxKind": "PropertySignature", - "name": "deliveryMethod", - "value": "DeliveryMethod.Http", + "name": "session", + "value": "Session", "description": "" }, { - "filePath": "../../node_modules/@shopify/shopify-api/lib/webhooks/types.d.ts", + "filePath": "src/server/config-types.ts", "syntaxKind": "PropertySignature", - "name": "callbackUrl", - "value": "string", + "name": "admin", + "value": "AdminApiContext", "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}" + "value": "export interface AfterAuthOptions<\n R extends ShopifyRestResources = ShopifyRestResources,\n> {\n session: Session;\n admin: AdminApiContext;\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}", + "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/webhooks/types.d.ts", - "name": "Http", - "value": "http" + "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/webhooks/types.d.ts", - "name": "EventBridge", - "value": "eventbridge" + "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/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", + "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/webhooks/types.d.ts", - "syntaxKind": "PropertySignature", - "name": "deliveryMethod", - "value": "DeliveryMethod.Http", + "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/webhooks/types.d.ts", - "syntaxKind": "PropertySignature", - "name": "callbackUrl", + "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/webhooks/types.d.ts", - "syntaxKind": "PropertySignature", - "name": "id", + "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": "", - "isOptional": true + "description": "" }, { - "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/session/session.d.ts", + "syntaxKind": "PropertyDeclaration", + "name": "onlineAccessInfo", + "value": "OnlineAccessInfo", + "description": "" }, { - "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": [ + "filePath": "../../node_modules/@shopify/shopify-api/lib/session/session.d.ts", + "syntaxKind": "MethodDeclaration", + "name": "isActive", + "value": "(scopes: string | string[] | AuthScopes) => boolean", + "description": "" + }, { - "name": "topic", - "description": "", - "value": "string", - "filePath": "../../node_modules/@shopify/shopify-api/lib/webhooks/types.d.ts" + "filePath": "../../node_modules/@shopify/shopify-api/lib/session/session.d.ts", + "syntaxKind": "MethodDeclaration", + "name": "isScopeChanged", + "value": "(scopes: string | string[] | AuthScopes) => boolean", + "description": "" }, { - "name": "shop_domain", - "description": "", - "value": "string", - "filePath": "../../node_modules/@shopify/shopify-api/lib/webhooks/types.d.ts" + "filePath": "../../node_modules/@shopify/shopify-api/lib/session/session.d.ts", + "syntaxKind": "MethodDeclaration", + "name": "isExpired", + "value": "(withinMillisecondsOfExpiry?: number) => boolean", + "description": "" }, { - "name": "body", - "description": "", - "value": "string", - "filePath": "../../node_modules/@shopify/shopify-api/lib/webhooks/types.d.ts" + "filePath": "../../node_modules/@shopify/shopify-api/lib/session/session.d.ts", + "syntaxKind": "MethodDeclaration", + "name": "toObject", + "value": "() => SessionParams", + "description": "" }, { - "name": "webhookId", - "description": "", - "value": "string", - "filePath": "../../node_modules/@shopify/shopify-api/lib/webhooks/types.d.ts" + "filePath": "../../node_modules/@shopify/shopify-api/lib/session/session.d.ts", + "syntaxKind": "MethodDeclaration", + "name": "equals", + "value": "(other: Session) => boolean", + "description": "" }, { - "name": "apiVersion", - "description": "", - "value": "string", - "isOptional": true, - "filePath": "../../node_modules/@shopify/shopify-api/lib/webhooks/types.d.ts" + "filePath": "../../node_modules/@shopify/shopify-api/lib/session/session.d.ts", + "syntaxKind": "MethodDeclaration", + "name": "toPropertyArray", + "value": "() => [string, string | number | boolean][]", + "description": "" } ], - "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;" + "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}" }, - "EventBridgeWebhookHandler": { - "filePath": "../../node_modules/@shopify/shopify-api/lib/webhooks/types.d.ts", - "name": "EventBridgeWebhookHandler", + "OnlineAccessInfo": { + "filePath": "../../node_modules/@shopify/shopify-api/lib/auth/oauth/types.d.ts", + "name": "OnlineAccessInfo", "description": "", "members": [ { - "filePath": "../../node_modules/@shopify/shopify-api/lib/webhooks/types.d.ts", + "filePath": "../../node_modules/@shopify/shopify-api/lib/auth/oauth/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", + "name": "expires_in", + "value": "number", "description": "" }, { @@ -9743,7 +9465,7 @@ "filePath": "src/server/clients/types.ts", "syntaxKind": "PropertySignature", "name": "headers", - "value": "{ [key: string]: any; }", + "value": "Record", "description": "", "isOptional": true }, @@ -9756,7 +9478,7 @@ "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}" + "value": "export interface GraphQLQueryOptions<\n Operation extends keyof Operations,\n Operations extends AllOperations,\n> {\n variables?: ApiClientRequestOptions['variables'];\n apiVersion?: ApiVersion;\n headers?: Record;\n tries?: number;\n}" }, "ApiVersion": { "filePath": "../../node_modules/@shopify/shopify-api/lib/types.d.ts", @@ -9898,7 +9620,7 @@ "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", + "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": { @@ -10073,10 +9795,10 @@ "returns": { "filePath": "src/server/types.ts", "description": "", - "name": "Promise", - "value": "Promise" + "name": "Promise", + "value": "Promise" }, - "value": "type RegisterWebhooks = (\n options: RegisterWebhooksOptions,\n) => Promise;" + "value": "type RegisterWebhooks = (\n options: RegisterWebhooksOptions,\n) => Promise;" }, "RegisterWebhooksOptions": { "filePath": "src/server/authenticate/webhooks/types.ts", @@ -11579,14 +11301,14 @@ { "filePath": "src/server/types.ts", "syntaxKind": "MethodSignature", - "name": "__@iterator@1657", + "name": "__@iterator@1695", "value": "() => IterableIterator", "description": "Iterator" }, { "filePath": "src/server/types.ts", "syntaxKind": "PropertySignature", - "name": "__@unscopables@1659", + "name": "__@unscopables@1697", "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." }, @@ -11743,7 +11465,7 @@ "filePath": "src/server/authenticate/webhooks/types.ts", "syntaxKind": "TypeAliasDeclaration", "name": "WebhookAdminContext", - "value": "FeatureEnabled extends true\n ? AdminApiContext\n : LegacyWebhookAdminApiContext", + "value": "FeatureEnabled extends true\n ? AdminApiContext\n : LegacyWebhookAdminApiContext", "description": "" }, "LegacyWebhookAdminApiContext": { @@ -13286,6 +13008,279 @@ "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", @@ -14442,28 +14437,39 @@ "type": "FutureFlags", "typeDefinitions": { "FutureFlags": { - "filePath": "../../node_modules/@shopify/shopify-api/future/flags.d.ts", + "filePath": "src/server/future/flags.ts", "name": "FutureFlags", "description": "", "members": [ { - "filePath": "../../node_modules/@shopify/shopify-api/future/flags.d.ts", + "filePath": "src/server/future/flags.ts", "syntaxKind": "PropertySignature", - "name": "unstable_tokenExchange", + "name": "v3_webhookAdminContext", "value": "boolean", - "description": "", - "isOptional": true + "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/shopify-api/future/flags.d.ts", + "filePath": "src/server/future/flags.ts", "syntaxKind": "PropertySignature", - "name": "unstable_lineItemBilling", + "name": "v3_authenticatePublic", "value": "boolean", - "description": "", - "isOptional": true + "description": "When enabled authenticate.public() will not work. Use authenticate.public.checkout() instead.", + "isOptional": true, + "defaultValue": "false" + }, + { + "filePath": "src/server/future/flags.ts", + "syntaxKind": "PropertySignature", + "name": "unstable_newEmbeddedAuthStrategy", + "value": "boolean", + "description": "When enabled, embedded apps will fetch access tokens via [token exchange](https://shopify.dev/docs/apps/auth/get-access-tokens/token-exchange). This assumes the app has scopes declared for [Shopify managing installation](https://shopify.dev/docs/apps/auth/installation#shopify-managed-installation).\n\nLearn more about this [new embedded app auth strategy](https://shopify.dev/docs/api/shopify-app-remix#embedded-auth-strategy).", + "isOptional": true, + "defaultValue": "false" } ], - "value": "export interface FutureFlags {\n unstable_tokenExchange?: boolean;\n unstable_lineItemBilling?: boolean;\n}" + "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\n /**\n * When enabled, embedded apps will fetch access tokens via [token exchange](https://shopify.dev/docs/apps/auth/get-access-tokens/token-exchange).\n * This assumes the app has scopes declared for [Shopify managing installation](https://shopify.dev/docs/apps/auth/installation#shopify-managed-installation).\n *\n * Learn more about this [new embedded app auth strategy](https://shopify.dev/docs/api/shopify-app-remix#embedded-auth-strategy).\n *\n * @default false\n */\n unstable_newEmbeddedAuthStrategy?: boolean;\n}" } } } @@ -15210,7 +15216,7 @@ "filePath": "src/server/clients/types.ts", "syntaxKind": "PropertySignature", "name": "headers", - "value": "{ [key: string]: any; }", + "value": "Record", "description": "", "isOptional": true }, @@ -15223,7 +15229,7 @@ "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}" + "value": "export interface GraphQLQueryOptions<\n Operation extends keyof Operations,\n Operations extends AllOperations,\n> {\n variables?: ApiClientRequestOptions['variables'];\n apiVersion?: ApiVersion;\n headers?: Record;\n tries?: number;\n}" }, "ApiVersion": { "filePath": "../../node_modules/@shopify/shopify-api/lib/types.d.ts", @@ -15767,7 +15773,7 @@ "filePath": "src/server/clients/types.ts", "syntaxKind": "PropertySignature", "name": "headers", - "value": "{ [key: string]: any; }", + "value": "Record", "description": "", "isOptional": true }, @@ -15780,7 +15786,7 @@ "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}" + "value": "export interface GraphQLQueryOptions<\n Operation extends keyof Operations,\n Operations extends AllOperations,\n> {\n variables?: ApiClientRequestOptions['variables'];\n apiVersion?: ApiVersion;\n headers?: Record;\n tries?: number;\n}" }, "ApiVersion": { "filePath": "../../node_modules/@shopify/shopify-api/lib/types.d.ts", diff --git a/packages/shopify-app-remix/docs/generated/generated_static_pages.json b/packages/shopify-app-remix/docs/generated/generated_static_pages.json index 820614e730..bab990e35a 100644 --- a/packages/shopify-app-remix/docs/generated/generated_static_pages.json +++ b/packages/shopify-app-remix/docs/generated/generated_static_pages.json @@ -163,6 +163,12 @@ "value": "", "description": "Returns the same `admin` context (`AdminApiContext`) from `authenticate.webhook` that is returned from `authenticate.admin`.\n\nSee [authenticate.webhook](/docs/api/shopify-app-remix/authenticate/webhook#example-admin) for more details.", "isOptional": true + }, + { + "name": "unstable_newEmbeddedAuthStrategy", + "value": "", + "description": "Embedded apps will fetch access tokens via token exchange. This assumes the app has declared scopes for Shopify managed installations.\n\nLearn more about this [new embedded app auth strategy](https://shopify.dev/docs/api/shopify-app-remix#embedded-auth-strategy).", + "isOptional": true } ] } @@ -416,7 +422,7 @@ "type": "Generic", "anchorLink": "auth-route", "title": "OAuth route", - "sectionContent": "To install an app or refresh tokens, you'll need to set up an [OAuth](docs/apps/auth/oauth) route. To do that, set up a [splat route](https://remix.run/docs/en/main/guides/routing#splats) that calls `authenticate.admin`.\n\nWhen that function is called, the package will start the OAuth process, and handle the callback from Shopify after it completes.\n\nThe default route is `/app/routes/auth/$.tsx`, but you can configure this route using the `authPathPrefix` option.", + "sectionContent": "> Tip: This is only applicable to non-embedded apps or legacy embedded apps that are **not** using the [new embedded app authorization strategy](#embedded-auth-strategy) for OAuth and installation flow. If you're building an embedded app, we **strongly** recommend using the [new embedded app authorization strategy](#embedded-auth-strategy)\n\nTo install an app or refresh tokens, you'll need to set up an [OAuth](docs/apps/auth/oauth) route. To do that, set up a [splat route](https://remix.run/docs/en/main/guides/routing#splats) that calls `authenticate.admin`.\n\nWhen that function is called, the package will start the OAuth process, and handle the callback from Shopify after it completes.\n\nThe default route is `/app/routes/auth/$.tsx`, but you can configure this route using the `authPathPrefix` option.", "codeblock": { "title": "Add OAuth route", "tabs": [ @@ -428,6 +434,22 @@ ] } }, + { + "type": "Generic", + "anchorLink": "embedded-auth-strategy", + "title": "New embedded app authorization strategy", + "sectionContent": "> Tip: This is available for embedded apps that are using [Shopify managed installation](https://shopify.dev/docs/apps/auth/installation#shopify-managed-installation).\n> If you're building an embedded app, we **strongly** recommend using this feature that utilizes Shopify managed install with [token exchange](https://shopify.dev/docs/apps/auth/get-access-tokens/token-exchange).\n\n We have introduced a new authorization and installation strategy for **embedded apps** that eliminates the redirects that were previously necessary. It replaces the legacy [authorization Code install and grant flow](https://shopify.dev/docs/apps/auth/get-access-tokens/authorization-code-grant).\n\nIt takes advantage of [Shopify managed installation](https://shopify.dev/docs/apps/auth/installation#shopify-managed-installation) to handle automatic app installations and scope updates, while using [token exchange](https://shopify.dev/docs/apps/auth/get-access-tokens/token-exchange) to get an access token for the logged-in user.\n\n > Note: Newly created Remix apps from the template after February 1st 2024 has this feature enabled by default.\n\n1. Enable [Shopify managed installation](https://shopify.dev/docs/apps/auth/installation#shopify-managed-installation) by configuring your scopes [through the Shopify CLI](https://shopify.dev/docs/apps/tools/cli/configuration).\n2. Enable the future flag `unstable_newEmbeddedAuthStrategy` in your app's server configuration file.\n3. Enjoy no-redirect OAuth flow, and app installation process.", + "codeblock": { + "title": "Enabling the new embedded auth strategy", + "tabs": [ + { + "title": "/app/shopify.server.ts", + "language": "ts", + "code": "// ... imports\nconst shopify = shopifyApp({\n // .. and the rest of the config\n isEmbeddedApp: true,\n future: {\n unstable_newEmbeddedAuthStrategy: true,\n },\n)};\n\n// ... exports\n" + } + ] + } + }, { "type": "Generic", "anchorLink": "app-provider", diff --git a/packages/shopify-app-remix/docs/staticPages/examples/index/embedded-app-auth-strategy-config.example.ts b/packages/shopify-app-remix/docs/staticPages/examples/index/embedded-app-auth-strategy-config.example.ts new file mode 100644 index 0000000000..4c3d2a9a61 --- /dev/null +++ b/packages/shopify-app-remix/docs/staticPages/examples/index/embedded-app-auth-strategy-config.example.ts @@ -0,0 +1,10 @@ +// ... imports +const shopify = shopifyApp({ + // .. and the rest of the config + isEmbeddedApp: true, + future: { + unstable_newEmbeddedAuthStrategy: true, + }, +)}; + +// ... exports diff --git a/packages/shopify-app-remix/docs/staticPages/future-flags.doc.ts b/packages/shopify-app-remix/docs/staticPages/future-flags.doc.ts index 26b1e56155..93a1fbbb5c 100644 --- a/packages/shopify-app-remix/docs/staticPages/future-flags.doc.ts +++ b/packages/shopify-app-remix/docs/staticPages/future-flags.doc.ts @@ -70,6 +70,14 @@ const data: LandingTemplateSchema = { '\n\nSee [authenticate.webhook](/docs/api/shopify-app-remix/authenticate/webhook#example-admin) for more details.', isOptional: true, }, + { + name: 'unstable_newEmbeddedAuthStrategy', + value: '', + description: + 'Embedded apps will fetch access tokens via token exchange. This assumes the app has declared scopes for Shopify managed installations.' + + '\n\nLearn more about this [new embedded app auth strategy](https://shopify.dev/docs/api/shopify-app-remix#embedded-auth-strategy).', + isOptional: true, + }, ], }, ], diff --git a/packages/shopify-app-remix/docs/staticPages/index.doc.ts b/packages/shopify-app-remix/docs/staticPages/index.doc.ts index 4a0dd634bd..6652f75b25 100644 --- a/packages/shopify-app-remix/docs/staticPages/index.doc.ts +++ b/packages/shopify-app-remix/docs/staticPages/index.doc.ts @@ -138,7 +138,9 @@ const data: LandingTemplateSchema = { anchorLink: 'auth-route', title: 'OAuth route', sectionContent: - "To install an app or refresh tokens, you'll need to set up an [OAuth](docs/apps/auth/oauth) route. To do that, set up a [splat route](https://remix.run/docs/en/main/guides/routing#splats) that calls `authenticate.admin`." + + "> Tip: This is only applicable to non-embedded apps or legacy embedded apps that are **not** using the [new embedded app authorization strategy](#embedded-auth-strategy) for OAuth and installation flow. If you're building an embedded app, we **strongly** recommend using the" + + " [new embedded app authorization strategy](#embedded-auth-strategy)" + + "\n\nTo install an app or refresh tokens, you'll need to set up an [OAuth](docs/apps/auth/oauth) route. To do that, set up a [splat route](https://remix.run/docs/en/main/guides/routing#splats) that calls `authenticate.admin`." + '\n\nWhen that function is called, the package will start the OAuth process, and handle the callback from Shopify after it completes.' + '\n\nThe default route is `/app/routes/auth/$.tsx`, but you can configure this route using the `authPathPrefix` option.', codeblock: { @@ -152,6 +154,35 @@ const data: LandingTemplateSchema = { ], }, }, + { + type: 'Generic', + anchorLink: 'embedded-auth-strategy', + title: 'New embedded app authorization strategy', + sectionContent: + "> Tip: This is available for embedded apps that are using [Shopify managed installation](https://shopify.dev/docs/apps/auth/installation#shopify-managed-installation)." + + "\n> If you're building an embedded app, we **strongly** recommend using this feature that utilizes Shopify managed install with [token exchange](https://shopify.dev/docs/apps/auth/get-access-tokens/token-exchange)." + + "\n\n We have introduced a new authorization and installation strategy for **embedded apps** that eliminates the redirects that were previously necessary." + + " It replaces the legacy [authorization Code install and grant flow](https://shopify.dev/docs/apps/auth/get-access-tokens/authorization-code-grant)." + + "\n\nIt takes advantage of [Shopify managed installation](https://shopify.dev/docs/apps/auth/installation#shopify-managed-installation)" + + " to handle automatic app installations and scope updates, while using" + + " [token exchange](https://shopify.dev/docs/apps/auth/get-access-tokens/token-exchange) to get an access token for the logged-in user." + + "\n\n > Note: Newly created Remix apps from the template after February 1st 2024 has this feature enabled by default." + + "\n\n1. Enable [Shopify managed installation](https://shopify.dev/docs/apps/auth/installation#shopify-managed-installation)" + + " by configuring your scopes [through the Shopify CLI](https://shopify.dev/docs/apps/tools/cli/configuration)." + + "\n2. Enable the future flag `unstable_newEmbeddedAuthStrategy` in your app's server configuration file." + + "\n3. Enjoy no-redirect OAuth flow, and app installation process.", + codeblock: { + title: 'Enabling the new embedded auth strategy', + tabs: [ + { + title: '/app/shopify.server.ts', + language: 'ts', + code: './examples/index/embedded-app-auth-strategy-config.example.ts', + + } + ], + } + }, { type: 'Generic', anchorLink: 'app-provider', diff --git a/packages/shopify-app-remix/src/server/future/flags.ts b/packages/shopify-app-remix/src/server/future/flags.ts index e8d75841ea..894c876e85 100644 --- a/packages/shopify-app-remix/src/server/future/flags.ts +++ b/packages/shopify-app-remix/src/server/future/flags.ts @@ -16,7 +16,10 @@ export interface FutureFlags { 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. + * When enabled, embedded apps will fetch access tokens via [token exchange](https://shopify.dev/docs/apps/auth/get-access-tokens/token-exchange). + * This assumes the app has scopes declared for [Shopify managing installation](https://shopify.dev/docs/apps/auth/installation#shopify-managed-installation). + * + * Learn more about this [new embedded app auth strategy](https://shopify.dev/docs/api/shopify-app-remix#embedded-auth-strategy). * * @default false */ diff --git a/packages/shopify-app-remix/src/server/shopify-app.doc.ts b/packages/shopify-app-remix/src/server/shopify-app.doc.ts index eb8f1594d6..fc87d8ab7c 100644 --- a/packages/shopify-app-remix/src/server/shopify-app.doc.ts +++ b/packages/shopify-app-remix/src/server/shopify-app.doc.ts @@ -20,6 +20,7 @@ const data: ReferenceEntityTemplateSchema = { 'Set future flags using the `future` configuration field to opt in to upcoming breaking changes.' + '\n\nWith this feature, you can prepare for major releases ahead of time, as well as try out new features before they are released.', type: 'FutureFlags', + filePath: 'src/server/future/flags.ts', }, ], jsDocTypeExamples: [ diff --git a/packages/shopify-app-session-storage-redis/package.json b/packages/shopify-app-session-storage-redis/package.json index 705c619208..a74c270cfe 100644 --- a/packages/shopify-app-session-storage-redis/package.json +++ b/packages/shopify-app-session-storage-redis/package.json @@ -32,7 +32,7 @@ "Redis" ], "dependencies": { - "redis": "^4.6.11" + "redis": "^4.6.12" }, "peerDependencies": { "@shopify/shopify-api": "^9.0.2", diff --git a/yarn.lock b/yarn.lock index 836a7eeb18..783218ee06 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2462,10 +2462,10 @@ resolved "https://registry.yarnpkg.com/@redis/bloom/-/bloom-1.2.0.tgz#d3fd6d3c0af3ef92f26767b56414a370c7b63b71" integrity sha512-HG2DFjYKbpNmVXsa0keLHp/3leGJz1mjh09f2RLGGLQZzSHpkmZWuwJbAvo3QcRY8p80m5+ZdXZdYOSBLlp7Cg== -"@redis/client@1.5.12": - version "1.5.12" - resolved "https://registry.yarnpkg.com/@redis/client/-/client-1.5.12.tgz#4c387727992152aea443b869de0ebb697f899187" - integrity sha512-/ZjE18HRzMd80eXIIUIPcH81UoZpwulbo8FmbElrjPqH0QC0SeIKu1BOU49bO5trM5g895kAjhvalt5h77q+4A== +"@redis/client@1.5.13": + version "1.5.13" + resolved "https://registry.yarnpkg.com/@redis/client/-/client-1.5.13.tgz#78786218fc1632ee4b5f7d73b8d456c95880e086" + integrity sha512-epkUM9D0Sdmt93/8Ozk43PNjLi36RZzG+d/T1Gdu5AI8jvghonTeLYV69WVWdilvFo+PYxbP0TZ0saMvr6nscQ== dependencies: cluster-key-slot "1.1.2" generic-pool "3.9.0" @@ -9130,13 +9130,13 @@ redent@^3.0.0: indent-string "^4.0.0" strip-indent "^3.0.0" -redis@^4.6.11: - version "4.6.11" - resolved "https://registry.yarnpkg.com/redis/-/redis-4.6.11.tgz#fad85e104545228f212259fd557c3e4f8eafcd3d" - integrity sha512-kg1Lt4NZLYkAjPOj/WcyIGWfZfnyfKo1Wg9YKVSlzhFwxpFIl3LYI8BWy1Ab963LLDsTz2+OwdsesHKljB3WMQ== +redis@^4.6.12: + version "4.6.12" + resolved "https://registry.yarnpkg.com/redis/-/redis-4.6.12.tgz#b607c93e9b0dd09e641f837092e9a68cc6ac6570" + integrity sha512-41Xuuko6P4uH4VPe5nE3BqXHB7a9lkFL0J29AlxKaIfD6eWO8VO/5PDF9ad2oS+mswMsfFxaM5DlE3tnXT+P8Q== dependencies: "@redis/bloom" "1.2.0" - "@redis/client" "1.5.12" + "@redis/client" "1.5.13" "@redis/graph" "1.1.1" "@redis/json" "1.0.6" "@redis/search" "1.1.6"